Merge "Move #nativeWaitForService into CommonConnectivityJni" into main
diff --git a/OWNERS_core_networking b/OWNERS_core_networking
index 4b73639..492ebd6 100644
--- a/OWNERS_core_networking
+++ b/OWNERS_core_networking
@@ -11,4 +11,4 @@
 yuyanghuang@google.com
 
 martinwu@google.com #{LAST_RESORT_SUGGESTION}
-satk@google.com #{LAST_RESORT_SUGGESTION}
\ No newline at end of file
+larsberg@google.com #{LAST_RESORT_SUGGESTION}
\ No newline at end of file
diff --git a/OWNERS_core_networking_xts b/OWNERS_core_networking_xts
index 60ca885..294b6e6 100644
--- a/OWNERS_core_networking_xts
+++ b/OWNERS_core_networking_xts
@@ -1,5 +1,5 @@
 lorenzo@google.com
-satk@google.com #{LAST_RESORT_SUGGESTION}
+larsberg@google.com #{LAST_RESORT_SUGGESTION}
 
 # For cherry-picks of CLs that are already merged in aosp/master, flaky test
 # fixes, or no-op refactors.
diff --git a/Tethering/apishim/31/com/android/networkstack/tethering/apishim/api31/BpfCoordinatorShimImpl.java b/Tethering/apishim/31/com/android/networkstack/tethering/apishim/api31/BpfCoordinatorShimImpl.java
index 9b6097d..23e0545 100644
--- a/Tethering/apishim/31/com/android/networkstack/tethering/apishim/api31/BpfCoordinatorShimImpl.java
+++ b/Tethering/apishim/31/com/android/networkstack/tethering/apishim/api31/BpfCoordinatorShimImpl.java
@@ -33,6 +33,7 @@
 import com.android.net.module.util.IBpfMap;
 import com.android.net.module.util.IBpfMap.ThrowingBiConsumer;
 import com.android.net.module.util.SharedLog;
+import com.android.net.module.util.Struct.S32;
 import com.android.net.module.util.bpf.Tether4Key;
 import com.android.net.module.util.bpf.Tether4Value;
 import com.android.net.module.util.bpf.TetherStatsKey;
@@ -42,10 +43,7 @@
 import com.android.networkstack.tethering.BpfCoordinator.Ipv6UpstreamRule;
 import com.android.networkstack.tethering.BpfUtils;
 import com.android.networkstack.tethering.Tether6Value;
-import com.android.networkstack.tethering.TetherDevKey;
-import com.android.networkstack.tethering.TetherDevValue;
 import com.android.networkstack.tethering.TetherDownstream6Key;
-import com.android.networkstack.tethering.TetherLimitKey;
 import com.android.networkstack.tethering.TetherLimitValue;
 import com.android.networkstack.tethering.TetherUpstream6Key;
 
@@ -89,11 +87,11 @@
 
     // BPF map of per-interface quota for tethering offload.
     @Nullable
-    private final IBpfMap<TetherLimitKey, TetherLimitValue> mBpfLimitMap;
+    private final IBpfMap<S32, TetherLimitValue> mBpfLimitMap;
 
     // BPF map of interface index mapping for XDP.
     @Nullable
-    private final IBpfMap<TetherDevKey, TetherDevValue> mBpfDevMap;
+    private final IBpfMap<S32, S32> mBpfDevMap;
 
     // Tracking IPv4 rule count while any rule is using the given upstream interfaces. Used for
     // reducing the BPF map iteration query. The count is increased or decreased when the rule is
@@ -305,7 +303,7 @@
         if (newLimit < rxBytes + txBytes) newLimit = QUOTA_UNLIMITED;
 
         try {
-            mBpfLimitMap.updateEntry(new TetherLimitKey(ifIndex), new TetherLimitValue(newLimit));
+            mBpfLimitMap.updateEntry(new S32(ifIndex), new TetherLimitValue(newLimit));
         } catch (ErrnoException e) {
             mLog.e("Fail to set quota " + quotaBytes + " for interface index " + ifIndex + ": ", e);
             return false;
@@ -350,7 +348,7 @@
         }
 
         try {
-            mBpfLimitMap.deleteEntry(new TetherLimitKey(ifIndex));
+            mBpfLimitMap.deleteEntry(new S32(ifIndex));
         } catch (ErrnoException e) {
             mLog.e("Could not delete limit for interface index " + ifIndex + ": ", e);
             return null;
@@ -470,7 +468,7 @@
     @Override
     public boolean addDevMap(int ifIndex) {
         try {
-            mBpfDevMap.updateEntry(new TetherDevKey(ifIndex), new TetherDevValue(ifIndex));
+            mBpfDevMap.updateEntry(new S32(ifIndex), new S32(ifIndex));
         } catch (ErrnoException e) {
             mLog.e("Could not add interface " + ifIndex + ": " + e);
             return false;
@@ -481,7 +479,7 @@
     @Override
     public boolean removeDevMap(int ifIndex) {
         try {
-            mBpfDevMap.deleteEntry(new TetherDevKey(ifIndex));
+            mBpfDevMap.deleteEntry(new S32(ifIndex));
         } catch (ErrnoException e) {
             mLog.e("Could not delete interface " + ifIndex + ": " + e);
             return false;
diff --git a/Tethering/src/com/android/networkstack/tethering/BpfCoordinator.java b/Tethering/src/com/android/networkstack/tethering/BpfCoordinator.java
index 57b7255..ef26e3b 100644
--- a/Tethering/src/com/android/networkstack/tethering/BpfCoordinator.java
+++ b/Tethering/src/com/android/networkstack/tethering/BpfCoordinator.java
@@ -460,11 +460,11 @@
         }
 
         /** Get limit BPF map. */
-        @Nullable public IBpfMap<TetherLimitKey, TetherLimitValue> getBpfLimitMap() {
+        @Nullable public IBpfMap<S32, TetherLimitValue> getBpfLimitMap() {
             if (!isAtLeastS()) return null;
             try {
                 return new BpfMap<>(TETHER_LIMIT_MAP_PATH,
-                    TetherLimitKey.class, TetherLimitValue.class);
+                    S32.class, TetherLimitValue.class);
             } catch (ErrnoException e) {
                 Log.e(TAG, "Cannot create limit map: " + e);
                 return null;
@@ -472,11 +472,11 @@
         }
 
         /** Get dev BPF map. */
-        @Nullable public IBpfMap<TetherDevKey, TetherDevValue> getBpfDevMap() {
+        @Nullable public IBpfMap<S32, S32> getBpfDevMap() {
             if (!isAtLeastS()) return null;
             try {
                 return new BpfMap<>(TETHER_DEV_MAP_PATH,
-                    TetherDevKey.class, TetherDevValue.class);
+                    S32.class, S32.class);
             } catch (ErrnoException e) {
                 Log.e(TAG, "Cannot create dev map: " + e);
                 return null;
@@ -1636,7 +1636,7 @@
     }
 
     private void dumpDevmap(@NonNull IndentingPrintWriter pw) {
-        try (IBpfMap<TetherDevKey, TetherDevValue> map = mDeps.getBpfDevMap()) {
+        try (IBpfMap<S32, S32> map = mDeps.getBpfDevMap()) {
             if (map == null) {
                 pw.println("No devmap support");
                 return;
@@ -1651,8 +1651,8 @@
                 // Only get upstream interface name. Just do the best to make the index readable.
                 // TODO: get downstream interface name because the index is either upstream or
                 // downstream interface in dev map.
-                pw.println(String.format("%d (%s) -> %d (%s)", k.ifIndex, getIfName(k.ifIndex),
-                        v.ifIndex, getIfName(v.ifIndex)));
+                pw.println(String.format("%d (%s) -> %d (%s)", k.val, getIfName(k.val),
+                        v.val, getIfName(v.val)));
             });
         } catch (ErrnoException | IOException e) {
             pw.println("Error dumping dev map: " + e);
diff --git a/Tethering/src/com/android/networkstack/tethering/TetherDevKey.java b/Tethering/src/com/android/networkstack/tethering/TetherDevKey.java
deleted file mode 100644
index 997080c..0000000
--- a/Tethering/src/com/android/networkstack/tethering/TetherDevKey.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * 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 com.android.networkstack.tethering;
-
-import com.android.net.module.util.Struct;
-import com.android.net.module.util.Struct.Field;
-import com.android.net.module.util.Struct.Type;
-
-/** The key of BpfMap which is used for mapping interface index. */
-public class TetherDevKey extends Struct {
-    @Field(order = 0, type = Type.S32)
-    public final int ifIndex;  // interface index
-
-    public TetherDevKey(final int ifIndex) {
-        this.ifIndex = ifIndex;
-    }
-}
diff --git a/Tethering/src/com/android/networkstack/tethering/TetherDevValue.java b/Tethering/src/com/android/networkstack/tethering/TetherDevValue.java
deleted file mode 100644
index b6e0c73..0000000
--- a/Tethering/src/com/android/networkstack/tethering/TetherDevValue.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * 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 com.android.networkstack.tethering;
-
-import com.android.net.module.util.Struct;
-import com.android.net.module.util.Struct.Field;
-import com.android.net.module.util.Struct.Type;
-
-/** The key of BpfMap which is used for mapping interface index. */
-public class TetherDevValue extends Struct {
-    @Field(order = 0, type = Type.S32)
-    public final int ifIndex;  // interface index
-
-    public TetherDevValue(final int ifIndex) {
-        this.ifIndex = ifIndex;
-    }
-}
diff --git a/Tethering/src/com/android/networkstack/tethering/TetherLimitKey.java b/Tethering/src/com/android/networkstack/tethering/TetherLimitKey.java
deleted file mode 100644
index 68d694a..0000000
--- a/Tethering/src/com/android/networkstack/tethering/TetherLimitKey.java
+++ /dev/null
@@ -1,31 +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.
- */
-
-package com.android.networkstack.tethering;
-
-import com.android.net.module.util.Struct;
-import com.android.net.module.util.Struct.Field;
-import com.android.net.module.util.Struct.Type;
-
-/** The key of BpfMap which is used for tethering per-interface limit. */
-public class TetherLimitKey extends Struct {
-    @Field(order = 0, type = Type.S32)
-    public final int ifindex;  // upstream interface index
-
-    public TetherLimitKey(final int ifindex) {
-        this.ifindex = ifindex;
-    }
-}
diff --git a/Tethering/tests/unit/src/com/android/networkstack/tethering/BpfCoordinatorTest.java b/Tethering/tests/unit/src/com/android/networkstack/tethering/BpfCoordinatorTest.java
index 8183256..48332ce 100644
--- a/Tethering/tests/unit/src/com/android/networkstack/tethering/BpfCoordinatorTest.java
+++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/BpfCoordinatorTest.java
@@ -482,10 +482,10 @@
             spy(new TestBpfMap<>(TetherUpstream6Key.class, Tether6Value.class));
     private final IBpfMap<TetherStatsKey, TetherStatsValue> mBpfStatsMap =
             spy(new TestBpfMap<>(TetherStatsKey.class, TetherStatsValue.class));
-    private final IBpfMap<TetherLimitKey, TetherLimitValue> mBpfLimitMap =
-            spy(new TestBpfMap<>(TetherLimitKey.class, TetherLimitValue.class));
-    private final IBpfMap<TetherDevKey, TetherDevValue> mBpfDevMap =
-            spy(new TestBpfMap<>(TetherDevKey.class, TetherDevValue.class));
+    private final IBpfMap<S32, TetherLimitValue> mBpfLimitMap =
+            spy(new TestBpfMap<>(S32.class, TetherLimitValue.class));
+    private final IBpfMap<S32, S32> mBpfDevMap =
+            spy(new TestBpfMap<>(S32.class, S32.class));
     private final IBpfMap<S32, S32> mBpfErrorMap =
             spy(new TestBpfMap<>(S32.class, S32.class));
     private BpfCoordinator.Dependencies mDeps =
@@ -559,12 +559,12 @@
                     }
 
                     @Nullable
-                    public IBpfMap<TetherLimitKey, TetherLimitValue> getBpfLimitMap() {
+                    public IBpfMap<S32, TetherLimitValue> getBpfLimitMap() {
                         return mBpfLimitMap;
                     }
 
                     @Nullable
-                    public IBpfMap<TetherDevKey, TetherDevValue> getBpfDevMap() {
+                    public IBpfMap<S32, S32> getBpfDevMap() {
                         return mBpfDevMap;
                     }
 
@@ -932,7 +932,7 @@
                         0L /* rxPackets */, 0L /* rxBytes */, 0L /* rxErrors */,
                         0L /* txPackets */, 0L /* txBytes */, 0L /* txErrors */));
             }
-            verifyWithOrder(inOrder, mBpfLimitMap).updateEntry(new TetherLimitKey(ifIndex),
+            verifyWithOrder(inOrder, mBpfLimitMap).updateEntry(new S32(ifIndex),
                     new TetherLimitValue(quotaBytes));
         } else {
             verifyWithOrder(inOrder, mNetd).tetherOffloadSetInterfaceQuota(ifIndex, quotaBytes);
@@ -955,7 +955,7 @@
         if (mDeps.isAtLeastS()) {
             inOrder.verify(mBpfStatsMap).getValue(new TetherStatsKey(ifIndex));
             inOrder.verify(mBpfStatsMap).deleteEntry(new TetherStatsKey(ifIndex));
-            inOrder.verify(mBpfLimitMap).deleteEntry(new TetherLimitKey(ifIndex));
+            inOrder.verify(mBpfLimitMap).deleteEntry(new S32(ifIndex));
         } else {
             inOrder.verify(mNetd).tetherOffloadGetAndClearStats(ifIndex);
         }
@@ -1972,10 +1972,10 @@
 
         dispatchIpv6UpstreamChanged(
                 coordinator, mIpServer, UPSTREAM_IFINDEX, UPSTREAM_IFACE, UPSTREAM_PREFIXES);
-        verify(mBpfDevMap).updateEntry(eq(new TetherDevKey(UPSTREAM_IFINDEX)),
-                eq(new TetherDevValue(UPSTREAM_IFINDEX)));
-        verify(mBpfDevMap).updateEntry(eq(new TetherDevKey(DOWNSTREAM_IFINDEX)),
-                eq(new TetherDevValue(DOWNSTREAM_IFINDEX)));
+        verify(mBpfDevMap).updateEntry(eq(new S32(UPSTREAM_IFINDEX)),
+                eq(new S32(UPSTREAM_IFINDEX)));
+        verify(mBpfDevMap).updateEntry(eq(new S32(DOWNSTREAM_IFINDEX)),
+                eq(new S32(DOWNSTREAM_IFINDEX)));
         clearInvocations(mBpfDevMap);
 
         // Adding the second downstream, only the second downstream ifindex is added to DevMap,
@@ -1983,10 +1983,10 @@
         coordinator.addIpServer(mIpServer2);
         dispatchIpv6UpstreamChanged(
                 coordinator, mIpServer2, UPSTREAM_IFINDEX, UPSTREAM_IFACE, UPSTREAM_PREFIXES);
-        verify(mBpfDevMap).updateEntry(eq(new TetherDevKey(DOWNSTREAM_IFINDEX2)),
-                eq(new TetherDevValue(DOWNSTREAM_IFINDEX2)));
-        verify(mBpfDevMap, never()).updateEntry(eq(new TetherDevKey(UPSTREAM_IFINDEX)),
-                eq(new TetherDevValue(UPSTREAM_IFINDEX)));
+        verify(mBpfDevMap).updateEntry(eq(new S32(DOWNSTREAM_IFINDEX2)),
+                eq(new S32(DOWNSTREAM_IFINDEX2)));
+        verify(mBpfDevMap, never()).updateEntry(eq(new S32(UPSTREAM_IFINDEX)),
+                eq(new S32(UPSTREAM_IFINDEX)));
     }
 
     @Test
@@ -1999,10 +1999,10 @@
                 .setMsgType(IPCTNL_MSG_CT_NEW)
                 .setProto(IPPROTO_TCP)
                 .build());
-        verify(mBpfDevMap).updateEntry(eq(new TetherDevKey(UPSTREAM_IFINDEX)),
-                eq(new TetherDevValue(UPSTREAM_IFINDEX)));
-        verify(mBpfDevMap).updateEntry(eq(new TetherDevKey(DOWNSTREAM_IFINDEX)),
-                eq(new TetherDevValue(DOWNSTREAM_IFINDEX)));
+        verify(mBpfDevMap).updateEntry(eq(new S32(UPSTREAM_IFINDEX)),
+                eq(new S32(UPSTREAM_IFINDEX)));
+        verify(mBpfDevMap).updateEntry(eq(new S32(DOWNSTREAM_IFINDEX)),
+                eq(new S32(DOWNSTREAM_IFINDEX)));
         clearInvocations(mBpfDevMap);
 
         mConsumer.accept(new TestConntrackEvent.Builder()
@@ -2737,8 +2737,8 @@
         // dumpDevmap
         coordinator.maybeAddUpstreamToLookupTable(UPSTREAM_IFINDEX, UPSTREAM_IFACE);
         mBpfDevMap.insertEntry(
-                new TetherDevKey(UPSTREAM_IFINDEX),
-                new TetherDevValue(UPSTREAM_IFINDEX));
+                new S32(UPSTREAM_IFINDEX),
+                new S32(UPSTREAM_IFINDEX));
 
         // dumpCounters
         // The error code is defined in packages/modules/Connectivity/bpf_progs/offload.h.
diff --git a/bpf/loader/NetBpfLoad.cpp b/bpf/loader/NetBpfLoad.cpp
index c8eb53c..93547a9 100644
--- a/bpf/loader/NetBpfLoad.cpp
+++ b/bpf/loader/NetBpfLoad.cpp
@@ -107,61 +107,9 @@
     return getBuildType() == "userdebug";
 }
 
-#define BPF_FS_PATH "/sys/fs/bpf/"
-
 static unsigned int page_size = static_cast<unsigned int>(getpagesize());
 
 typedef struct {
-    const char* name;
-    enum bpf_prog_type type;
-    enum bpf_attach_type attach_type;
-} sectionType;
-
-/*
- * Map section name prefixes to program types, the section name will be:
- *   SECTION(<prefix>/<name-of-program>)
- * For example:
- *   SECTION("tracepoint/sched_switch_func") where sched_switch_funcs
- * is the name of the program, and tracepoint is the type.
- *
- * However, be aware that you should not be directly using the SECTION() macro.
- * Instead use the DEFINE_(BPF|XDP)_(PROG|MAP)... & LICENSE macros.
- *
- * Programs shipped inside the tethering apex should be limited to networking stuff,
- * as KPROBE, PERF_EVENT, TRACEPOINT are dangerous to use from mainline updatable code,
- * since they are less stable abi/api and may conflict with platform uses of bpf.
- */
-sectionType sectionNameTypes[] = {
-        {"bind4/",             BPF_PROG_TYPE_CGROUP_SOCK_ADDR, BPF_CGROUP_INET4_BIND},
-        {"bind6/",             BPF_PROG_TYPE_CGROUP_SOCK_ADDR, BPF_CGROUP_INET6_BIND},
-        {"cgroupskb/",         BPF_PROG_TYPE_CGROUP_SKB},
-        {"cgroupsock/",        BPF_PROG_TYPE_CGROUP_SOCK},
-        {"cgroupsockcreate/",  BPF_PROG_TYPE_CGROUP_SOCK,      BPF_CGROUP_INET_SOCK_CREATE},
-        {"cgroupsockrelease/", BPF_PROG_TYPE_CGROUP_SOCK,      BPF_CGROUP_INET_SOCK_RELEASE},
-        {"connect4/",          BPF_PROG_TYPE_CGROUP_SOCK_ADDR, BPF_CGROUP_INET4_CONNECT},
-        {"connect6/",          BPF_PROG_TYPE_CGROUP_SOCK_ADDR, BPF_CGROUP_INET6_CONNECT},
-        {"egress/",            BPF_PROG_TYPE_CGROUP_SKB,       BPF_CGROUP_INET_EGRESS},
-        {"getsockopt/",        BPF_PROG_TYPE_CGROUP_SOCKOPT,   BPF_CGROUP_GETSOCKOPT},
-        {"ingress/",           BPF_PROG_TYPE_CGROUP_SKB,       BPF_CGROUP_INET_INGRESS},
-        {"postbind4/",         BPF_PROG_TYPE_CGROUP_SOCK,      BPF_CGROUP_INET4_POST_BIND},
-        {"postbind6/",         BPF_PROG_TYPE_CGROUP_SOCK,      BPF_CGROUP_INET6_POST_BIND},
-        {"recvmsg4/",          BPF_PROG_TYPE_CGROUP_SOCK_ADDR, BPF_CGROUP_UDP4_RECVMSG},
-        {"recvmsg6/",          BPF_PROG_TYPE_CGROUP_SOCK_ADDR, BPF_CGROUP_UDP6_RECVMSG},
-        {"schedact/",          BPF_PROG_TYPE_SCHED_ACT},
-        {"schedcls/",          BPF_PROG_TYPE_SCHED_CLS},
-        {"sendmsg4/",          BPF_PROG_TYPE_CGROUP_SOCK_ADDR, BPF_CGROUP_UDP4_SENDMSG},
-        {"sendmsg6/",          BPF_PROG_TYPE_CGROUP_SOCK_ADDR, BPF_CGROUP_UDP6_SENDMSG},
-        {"setsockopt/",        BPF_PROG_TYPE_CGROUP_SOCKOPT,   BPF_CGROUP_SETSOCKOPT},
-        {"skfilter/",          BPF_PROG_TYPE_SOCKET_FILTER},
-        {"sockops/",           BPF_PROG_TYPE_SOCK_OPS,         BPF_CGROUP_SOCK_OPS},
-        {"sysctl",             BPF_PROG_TYPE_CGROUP_SYSCTL,    BPF_CGROUP_SYSCTL},
-        {"xdp/",               BPF_PROG_TYPE_XDP},
-};
-
-typedef struct {
-    enum bpf_prog_type type;
-    enum bpf_attach_type attach_type;
-    string name; // The canonicalized section name.
     string program_name;
     vector<char> data;
     vector<char> rel_data;
@@ -404,37 +352,35 @@
     entries = shTable.size();
 
     vector<struct bpf_prog_def> pd;
-    ret = elfObj.readSectionByName("progs", pd);
+    ret = elfObj.readSectionByName(".android_progs", pd);
     if (ret) return ret;
     vector<string> progDefNames;
-    ret = elfObj.getSectionSymNames("progs", progDefNames);
+    ret = elfObj.getSectionSymNames(".android_progs", progDefNames);
     if (!pd.empty() && ret) return ret;
 
     for (int i = 0; i < entries; i++) {
         string name;
         codeSection cs_temp;
-        cs_temp.type = BPF_PROG_TYPE_UNSPEC;
 
         ret = elfObj.getSymName(shTable[i].sh_name, name);
         if (ret) return ret;
 
-        // This must be done before '/' is replaced with '_'.
-        for (auto& snt : sectionNameTypes) {
-            if (StartsWith(name, snt.name)) {
-                cs_temp.type = snt.type;
-                cs_temp.attach_type = snt.attach_type;
-                break;
-            }
-        }
+        // all we want to process is sections FOO/BAR, but:
+        // - section 0 has an empty name (experimentally observed)
+        // - .relFOO/BAR would break us later (relocations)
+        // - 'license' is special, but doesn't have a /
+        if (name[0] == '.') continue;
 
-        if (cs_temp.type == BPF_PROG_TYPE_UNSPEC) continue;
+        // Find the first slash
+        size_t first_slash_pos = name.find('/');
+
+        // Ignore sections without a /  (basically 'license' section)
+        if (first_slash_pos == std::string::npos) continue;
 
         string oldName = name;
+        name[first_slash_pos] = '_';
 
-        // convert all slashes to underscores
-        std::replace(name.begin(), name.end(), '/', '_');
-
-        cs_temp.name = name;
+        if (name.find('/') != std::string::npos) abort(); // There should only be one!
 
         ret = elfObj.readSectionByIdx(i, cs_temp.data);
         if (ret) return ret;
@@ -451,6 +397,8 @@
             }
         }
 
+        if (!cs_temp.prog_def) abort();
+
         // Check for rel section
         if (cs_temp.data.size() > 0 && i < entries) {
             ret = elfObj.getSymName(shTable[i + 1].sh_name, name);
@@ -1013,8 +961,7 @@
     }
 }
 
-static int pinProg(const borrowed_fd& fd, const struct bpf_prog_def& progDef,
-                   const string& progPinLoc) {
+static int pinProg(const borrowed_fd& fd, const struct bpf_prog_def& progDef) {
     int ret;
     if (progDef.create_location[0]) {
         ret = bpfFdPin(fd, progDef.create_location);
@@ -1024,37 +971,37 @@
             return -err;
         }
         ret = renameat2(AT_FDCWD, progDef.create_location,
-                        AT_FDCWD, progPinLoc.c_str(), RENAME_NOREPLACE);
+                        AT_FDCWD, progDef.pin_location, RENAME_NOREPLACE);
         if (ret) {
             const int err = errno;
-            ALOGE("rename %s %s -> %d [%d:%s]", progDef.create_location, progPinLoc.c_str(), ret,
+            ALOGE("rename %s %s -> %d [%d:%s]", progDef.create_location, progDef.pin_location, ret,
                   err, strerror(err));
             return -err;
         }
     } else {
-        ret = bpfFdPin(fd, progPinLoc.c_str());
+        ret = bpfFdPin(fd, progDef.pin_location);
         if (ret) {
             const int err = errno;
-            ALOGE("create %s -> %d [%d:%s]", progPinLoc.c_str(), ret, err, strerror(err));
+            ALOGE("create %s -> %d [%d:%s]", progDef.pin_location, ret, err, strerror(err));
             return -err;
         }
     }
-    if (chmod(progPinLoc.c_str(), 0440)) {
+    if (chmod(progDef.pin_location, 0440)) {
         const int err = errno;
-        ALOGE("chmod %s 0440 -> [%d:%s]", progPinLoc.c_str(), err, strerror(err));
+        ALOGE("chmod %s 0440 -> [%d:%s]", progDef.pin_location, err, strerror(err));
         return -err;
     }
-    if (chown(progPinLoc.c_str(), (uid_t)progDef.uid,
+    if (chown(progDef.pin_location, (uid_t)progDef.uid,
               (gid_t)progDef.gid)) {
         const int err = errno;
-        ALOGE("chown %s %d %d -> [%d:%s]", progPinLoc.c_str(), progDef.uid,
+        ALOGE("chown %s %d %d -> [%d:%s]", progDef.pin_location, progDef.uid,
               progDef.gid, err, strerror(err));
         return -err;
     }
     return 0;
 }
 
-static int validateProg(const borrowed_fd& fd, string& progPinLoc,
+static int validateProg(const borrowed_fd& fd, const char* const progPinLoc,
                         const unsigned int bpfloader_ver) {
     if (!isAtLeastKernelVersion(4, 14, 0)) {
         return 0;
@@ -1079,10 +1026,10 @@
         ALOGE("bpfGetFdXlatProgLen failed, ret: %d", err);
         return -err;
     }
-    ALOGI("prog %s id %d len jit:%d xlat:%d", progPinLoc.c_str(), progId, jitLen, xlatLen);
+    ALOGI("prog %s id %d len jit:%d xlat:%d", progPinLoc, progId, jitLen, xlatLen);
 
     if (!jitLen && bpfloader_ver >= BPFLOADER_MAINLINE_25Q2_VERSION) {
-        ALOGE("Kernel eBPF JIT failure for %s", progPinLoc.c_str());
+        ALOGE("Kernel eBPF JIT failure for %s", progPinLoc);
         return -ENOTSUP;
     }
     return 0;
@@ -1100,55 +1047,48 @@
     for (int i = 0; i < (int)cs.size(); i++) {
         unique_fd& fd = cs[i].prog_fd;
         int ret;
-        string name = cs[i].name;
 
         if (!cs[i].prog_def.has_value()) {
-            ALOGE("[%d] '%s' missing program definition! bad bpf.o build?", i, name.c_str());
+            ALOGE("[%d] missing program definition! bad bpf.o build?", i);
             return -EINVAL;
         }
 
         unsigned min_kver = cs[i].prog_def->min_kver;
         unsigned max_kver = cs[i].prog_def->max_kver;
-        ALOGD("cs[%d].name:%s min_kver:%x .max_kver:%x (kvers:%x)", i, name.c_str(), min_kver,
-             max_kver, kvers);
+        ALOGD("cs[%d].name:%s min_kver:%x .max_kver:%x (kvers:%x)",
+             i, cs[i].prog_def->name(), min_kver, max_kver, kvers);
         if (kvers < min_kver) continue;
         if (kvers >= max_kver) continue;
 
         unsigned bpfMinVer = cs[i].prog_def->bpfloader_min_ver;
         unsigned bpfMaxVer = cs[i].prog_def->bpfloader_max_ver;
 
-        ALOGD("cs[%d].name:%s requires bpfloader version [0x%05x,0x%05x)", i, name.c_str(),
-              bpfMinVer, bpfMaxVer);
+        ALOGD("cs[%d].name:%s requires bpfloader version [0x%05x,0x%05x)",
+              i, cs[i].prog_def->name(), bpfMinVer, bpfMaxVer);
         if (bpfloader_ver < bpfMinVer) continue;
         if (bpfloader_ver >= bpfMaxVer) continue;
 
-        // strip any potential $foo suffix
-        // this can be used to provide duplicate programs
-        // conditionally loaded based on running kernel version
-        name = name.substr(0, name.find_last_of('$'));
-
         bool reuse = false;
-        string progPinLoc = string(cs[i].prog_def->pin_prefix) + name;
-        if (access(progPinLoc.c_str(), F_OK) == 0) {
-            fd.reset(retrieveProgram(progPinLoc.c_str()));
-            ALOGD("New bpf prog load reusing prog %s, ret: %d (%s)", progPinLoc.c_str(), fd.get(),
+        if (access(cs[i].prog_def->pin_location, F_OK) == 0) {
+            fd.reset(retrieveProgram(cs[i].prog_def->pin_location));
+            ALOGD("New bpf prog load reusing prog %s, ret: %d (%s)", cs[i].prog_def->pin_location, fd.get(),
                   !fd.ok() ? std::strerror(errno) : "ok");
             reuse = true;
         } else {
             static char log_buf[1 << 20];  // 1 MiB logging buffer
 
             union bpf_attr req = {
-              .prog_type = cs[i].type,
+              .prog_type = cs[i].prog_def->type,
               .insn_cnt = static_cast<__u32>(cs[i].data.size() / sizeof(struct bpf_insn)),
               .insns = ptr_to_u64(cs[i].data.data()),
               .license = ptr_to_u64(license.c_str()),
               .log_level = 1,
               .log_size = sizeof(log_buf),
               .log_buf = ptr_to_u64(log_buf),
-              .expected_attach_type = cs[i].attach_type,
+              .expected_attach_type = cs[i].prog_def->attach_type,
             };
             if (isAtLeastKernelVersion(4, 15, 0))
-                strlcpy(req.prog_name, cs[i].name.c_str(), sizeof(req.prog_name));
+                strlcpy(req.prog_name, cs[i].prog_def->name(), sizeof(req.prog_name));
             fd.reset(bpf(BPF_PROG_LOAD, req));
 
             // Kernel should have NULL terminated the log buffer, but force it anyway for safety
@@ -1161,7 +1101,7 @@
             bool log_oneline = !strchr(log_buf, '\n');
 
             ALOGD("BPF_PROG_LOAD call for %s (%s) returned '%s' fd: %d (%s)", elfObj.path,
-                  cs[i].name.c_str(), log_oneline ? log_buf : "{multiline}",
+                  cs[i].prog_def->name(), log_oneline ? log_buf : "{multiline}",
                   fd.get(), !fd.ok() ? std::strerror(errno) : "ok");
 
             if (!fd.ok()) {
@@ -1176,20 +1116,20 @@
 
                 if (cs[i].prog_def->optional) {
                     ALOGW("failed program %s is marked optional - continuing...",
-                          cs[i].name.c_str());
+                          cs[i].prog_def->name());
                     continue;
                 }
-                ALOGE("non-optional program %s failed to load.", cs[i].name.c_str());
+                ALOGE("non-optional program %s failed to load.", cs[i].prog_def->name());
             }
         }
 
         if (!fd.ok()) return fd.get();
 
         if (!reuse) {
-            ret = pinProg(fd, cs[i].prog_def.value(), progPinLoc);
+            ret = pinProg(fd, cs[i].prog_def.value());
             if (ret) return ret;
         }
-        ret = validateProg(fd, progPinLoc, bpfloader_ver);
+        ret = validateProg(fd, cs[i].prog_def->pin_location, bpfloader_ver);
         if (ret) return ret;
     }
 
@@ -1239,9 +1179,8 @@
     unsigned kvers = kernelVersion();
 
     for (int i = 0; i < (int)cs.size(); i++) {
-        string name = cs[i].name;
         if (!cs[i].prog_def.has_value()) {
-            ALOGE("[%d] '%s' missing program definition! bad bpf.o build?", i, name.c_str());
+            ALOGE("[%d] missing program definition! bad bpf.o build?", i);
             return -EINVAL;
         }
         string program_name = cs[i].program_name;
@@ -1255,7 +1194,7 @@
         unsigned max_kver = cs[i].prog_def->max_kver;
         if (kvers < min_kver || kvers >= max_kver) {
             ALOGD("skipping prog %s: kernel version 0x%x is outside required range [0x%x, 0x%x)",
-                  name.c_str(), kvers, min_kver, max_kver);
+                  cs[i].prog_def->name(), kvers, min_kver, max_kver);
             bpf_program__set_autoload(prog, false);
             continue;
         }
@@ -1264,7 +1203,7 @@
         unsigned bpfMaxVer = cs[i].prog_def->bpfloader_max_ver;
         if (bpfloader_ver < bpfMinVer || bpfloader_ver >= bpfMaxVer) {
             ALOGD("skipping prog %s: bpfloader 0x%05x is outside required range [0x%05x, 0x%05x)",
-                  name.c_str(), bpfloader_ver, bpfMinVer, bpfMaxVer);
+                  cs[i].prog_def->name(), bpfloader_ver, bpfMinVer, bpfMaxVer);
             bpf_program__set_autoload(prog, false);
             continue;
         }
@@ -1275,8 +1214,8 @@
             return -1;
         }
 
-        bpf_program__set_type(prog, cs[i].type);
-        bpf_program__set_expected_attach_type(prog, cs[i].attach_type);
+        bpf_program__set_type(prog, cs[i].prog_def->type);
+        bpf_program__set_expected_attach_type(prog, cs[i].prog_def->attach_type);
     }
     return 0;
 }
@@ -1316,19 +1255,16 @@
         // This program was skipped
         if (!bpf_program__autoload(prog)) continue;
 
-        string name = cs[i].name;
-        name = name.substr(0, name.find_last_of('$'));
-        string progPinLoc = string(cs[i].prog_def->pin_prefix) + name;
-        if (access(progPinLoc.c_str(), F_OK) == 0) {
+        if (access(cs[i].prog_def->pin_location, F_OK) == 0) {
             // TODO: Skip loading lower priority program
-            ALOGI("Higher priority program is already pinned, skip pinning %s", cs[i].name.c_str());
+            ALOGI("Higher priority program is already pinned, skip pinning %s", cs[i].prog_def->name());
             continue;
         }
 
         int fd = bpf_program__fd(prog);
-        ret = pinProg(fd, cs[i].prog_def.value(), progPinLoc);
+        ret = pinProg(fd, cs[i].prog_def.value());
         if (ret) return ret;
-        ret = validateProg(fd, progPinLoc, bpfloader_ver);
+        ret = validateProg(fd, cs[i].prog_def->pin_location, bpfloader_ver);
         if (ret) return ret;
     }
     return 0;
@@ -1426,47 +1362,30 @@
     abort();  // can only hit this if permissions (likely selinux) are screwed up
 }
 
+static bool loadObject(const unsigned int bpfloader_ver,
+                      const char* const progPath, const bool useLibbpf = false) {
+    if (useLibbpf ? loadProgByLibbpf(progPath, bpfloader_ver) :
+                          loadProg(progPath, bpfloader_ver)) {
+        ALOGE("Failed to load object: %s, libbpf: %d", progPath, useLibbpf);
+        return false;
+    }
+    ALOGD("Loaded object: %s, libbpf: %d", progPath, useLibbpf);
+    return true;
+}
+
 #define APEXROOT "/apex/com.android.tethering"
 #define BPFROOT APEXROOT "/etc/bpf/mainline/"
 
-static int loadObject(const unsigned int bpfloader_ver,
-                      const char* const fname, const bool useLibbpf = false) {
-    string progPath = string(BPFROOT) + fname;
-    int ret = useLibbpf ? loadProgByLibbpf(progPath.c_str(), bpfloader_ver) :
-                          loadProg(progPath.c_str(), bpfloader_ver);
-    if (ret) {
-        ALOGE("Failed to load object: %s, ret: %s, libbpf: %d",
-              progPath.c_str(), std::strerror(-ret), useLibbpf);
-        return 1;
-    }
-    ALOGD("Loaded object: %s, libbpf: %d", progPath.c_str(), useLibbpf);
-    return 0;
-}
-
-static int loadAllObjects(const unsigned int bpfloader_ver) {
-    // S+ Tethering mainline module (network_stack): tether offload
-    // loads under /sys/fs/bpf/tethering:
-    if (loadObject(bpfloader_ver, "offload.o")) return 1;
-    if (loadObject(bpfloader_ver, "test.o", isAtLeast25Q3)) return 1;
+static bool loadAllObjects(const unsigned int bpfloader_ver) {
+    bool libbpf = isAtLeast25Q3;
+    if (!loadObject(bpfloader_ver, BPFROOT "offload.o")) return false;
+    if (!loadObject(bpfloader_ver, BPFROOT "test.o", libbpf)) return false;
     if (isAtLeastT) {
-        // T+ Tethering mainline module loads under:
-        // /sys/fs/bpf/net_shared: shared with netd & system server
-        if (loadObject(bpfloader_ver, "clatd.o", isAtLeast25Q3)) return 1;
-        if (loadObject(bpfloader_ver, "dscpPolicy.o", isAtLeast25Q3)) return 1;
-
-        // /sys/fs/bpf/netd_shared: shared with netd & system server
-        // - netutils_wrapper (for iptables xt_bpf) has access to programs
-
-        // WARNING: Android T+ non-updatable netd depends on both of the
-        // 'netd_shared' & 'netd' strings for xt_bpf programs it loads
-        if (loadObject(bpfloader_ver, "netd.o", isAtLeast25Q3)) return 1;
-
-        // /sys/fs/bpf/netd_readonly: shared with netd & system server
-        // - netutils_wrapper has no access, netd has read only access
-
-        // /sys/fs/bpf/net_private: not shared, just network_stack
+        if (!loadObject(bpfloader_ver, BPFROOT "clatd.o", libbpf)) return false;
+        if (!loadObject(bpfloader_ver, BPFROOT "dscpPolicy.o", libbpf)) return false;
+        if (!loadObject(bpfloader_ver, BPFROOT "netd.o", libbpf)) return false;
     }
-    return 0;
+    return true;
 }
 
 static bool createDir(const char* const dir) {
@@ -1952,7 +1871,7 @@
     }
 
     // Load all ELF objects, create programs and maps, and pin them
-    if (loadAllObjects(bpfloader_ver)) {
+    if (!loadAllObjects(bpfloader_ver)) {
         ALOGE("=== CRITICAL FAILURE LOADING BPF PROGRAMS ===");
         ALOGE("If this triggers reliably, you're probably missing kernel options or patches.");
         ALOGE("If this triggers randomly, you might be hitting some memory allocation "
diff --git a/bpf/progs/bpf_net_helpers.h b/bpf/progs/bpf_net_helpers.h
index 4085ed4..a41d8af 100644
--- a/bpf/progs/bpf_net_helpers.h
+++ b/bpf/progs/bpf_net_helpers.h
@@ -92,15 +92,15 @@
 
 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 long (*bpf_skb_pull_data)(struct __sk_buff* skb, __u32 len) = (void*)BPF_FUNC_skb_pull_data;
 
-static int (*bpf_skb_load_bytes)(const struct __sk_buff* skb, int off, void* to,
-                                 int len) = (void*)BPF_FUNC_skb_load_bytes;
+static long (*bpf_skb_load_bytes)(const struct __sk_buff* skb, int off, void* to,
+                                  int len) = (void*)BPF_FUNC_skb_load_bytes;
 
-static int (*bpf_skb_load_bytes_relative)(const struct __sk_buff* skb, int off, void* to, int len,
-                                          int start_hdr) = (void*)BPF_FUNC_skb_load_bytes_relative;
+static long (*bpf_skb_load_bytes_relative)(const struct __sk_buff* skb, int off, void* to, int len,
+                                           int start_hdr) = (void*)BPF_FUNC_skb_load_bytes_relative;
 
-static int (*bpf_skb_store_bytes)(struct __sk_buff* skb, __u32 offset, const void* from, __u32 len,
+static long (*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,
@@ -108,20 +108,20 @@
 
 static int64_t (*bpf_csum_update)(struct __sk_buff* skb, __wsum csum) = (void*)BPF_FUNC_csum_update;
 
-static int (*bpf_skb_change_proto)(struct __sk_buff* skb, __be16 proto,
-                                   __u64 flags) = (void*)BPF_FUNC_skb_change_proto;
-static int (*bpf_l3_csum_replace)(struct __sk_buff* skb, __u32 offset, __u64 from, __u64 to,
-                                  __u64 flags) = (void*)BPF_FUNC_l3_csum_replace;
-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 long (*bpf_skb_change_proto)(struct __sk_buff* skb, __be16 proto,
+                                    __u64 flags) = (void*)BPF_FUNC_skb_change_proto;
+static long (*bpf_l3_csum_replace)(struct __sk_buff* skb, __u32 offset, __u64 from, __u64 to,
+                                   __u64 flags) = (void*)BPF_FUNC_l3_csum_replace;
+static long (*bpf_l4_csum_replace)(struct __sk_buff* skb, __u32 offset, __u64 from, __u64 to,
+                                   __u64 flags) = (void*)BPF_FUNC_l4_csum_replace;
+static long (*bpf_redirect)(__u32 ifindex, __u64 flags) = (void*)BPF_FUNC_redirect;
+static long (*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;
-static int (*bpf_skb_adjust_room)(struct __sk_buff* skb, __s32 len_diff, __u32 mode,
-                                  __u64 flags) = (void*)BPF_FUNC_skb_adjust_room;
+static long (*bpf_skb_change_head)(struct __sk_buff* skb, __u32 head_room,
+                                   __u64 flags) = (void*)BPF_FUNC_skb_change_head;
+static long (*bpf_skb_adjust_room)(struct __sk_buff* skb, __s32 len_diff, __u32 mode,
+                                   __u64 flags) = (void*)BPF_FUNC_skb_adjust_room;
 
 // Android only supports little endian architectures
 #define htons(x) (__builtin_constant_p(x) ? ___constant_swab16(x) : __builtin_bswap16(x))
diff --git a/bpf/progs/clatd.c b/bpf/progs/clatd.c
index 0560774..017d742 100644
--- a/bpf/progs/clatd.c
+++ b/bpf/progs/clatd.c
@@ -247,29 +247,29 @@
     return TC_ACT_PIPE;
 }
 
-DEFINE_BPF_PROG_KVER("schedcls/ingress6/clat_ether$4_14", AID_ROOT, AID_SYSTEM, sched_cls_ingress6_clat_ether_4_14, KVER_4_14)
+DEFINE_BPF_PROG_KVER(schedcls, ingress6_clat_ether, 4_14, AID_SYSTEM, 4_14)
 (struct __sk_buff* skb) {
     return nat64(skb, ETHER, KVER_4_14);
 }
 
-DEFINE_BPF_PROG_KVER_RANGE("schedcls/ingress6/clat_ether$4_9", AID_ROOT, AID_SYSTEM, sched_cls_ingress6_clat_ether_4_9, KVER_NONE, KVER_4_14)
+DEFINE_BPF_PROG_KVER_RANGE(schedcls, ingress6_clat_ether, 4_9, AID_SYSTEM, 4_9, 4_14)
 (struct __sk_buff* skb) {
-    return nat64(skb, ETHER, KVER_NONE);
+    return nat64(skb, ETHER, KVER_4_9);
 }
 
-DEFINE_BPF_PROG_KVER("schedcls/ingress6/clat_rawip$4_14", AID_ROOT, AID_SYSTEM, sched_cls_ingress6_clat_rawip_4_14, KVER_4_14)
+DEFINE_BPF_PROG_KVER(schedcls, ingress6_clat_rawip, 4_14, AID_SYSTEM, 4_14)
 (struct __sk_buff* skb) {
     return nat64(skb, RAWIP, KVER_4_14);
 }
 
-DEFINE_BPF_PROG_KVER_RANGE("schedcls/ingress6/clat_rawip$4_9", AID_ROOT, AID_SYSTEM, sched_cls_ingress6_clat_rawip_4_9, KVER_NONE, KVER_4_14)
+DEFINE_BPF_PROG_KVER_RANGE(schedcls, ingress6_clat_rawip, 4_9, AID_SYSTEM, 4_9, 4_14)
 (struct __sk_buff* skb) {
-    return nat64(skb, RAWIP, KVER_NONE);
+    return nat64(skb, RAWIP, KVER_4_9);
 }
 
 DEFINE_BPF_MAP_GRW(clat_egress4_map, HASH, ClatEgress4Key, ClatEgress4Value, 16, AID_SYSTEM)
 
-DEFINE_BPF_PROG("schedcls/egress4/clat_rawip", AID_ROOT, AID_SYSTEM, sched_cls_egress4_clat_rawip)
+DEFINE_BPF_PROG(schedcls, egress4_clat_rawip, , AID_SYSTEM)
 (struct __sk_buff* skb) {
     // Must be meta-ethernet IPv4 frame
     if (skb->protocol != htons(ETH_P_IP)) return TC_ACT_PIPE;
diff --git a/bpf/progs/dscpPolicy.c b/bpf/progs/dscpPolicy.c
index 3305aad..8b98b78 100644
--- a/bpf/progs/dscpPolicy.c
+++ b/bpf/progs/dscpPolicy.c
@@ -264,8 +264,7 @@
     return;
 }
 
-DEFINE_BPF_PROG_KVER("schedcls/set_dscp_ether", AID_ROOT, AID_SYSTEM, schedcls_set_dscp_ether,
-                     KVER_5_15)
+DEFINE_BPF_PROG_KVER(schedcls, set_dscp_ether, , AID_SYSTEM, 5_15)
 (struct __sk_buff* skb) {
     if (skb->pkt_type != PACKET_HOST) return TC_ACT_PIPE;
 
diff --git a/bpf/progs/include/bpf_helpers.h b/bpf/progs/include/bpf_helpers.h
index a0c12d0..b68ad2b 100644
--- a/bpf/progs/include/bpf_helpers.h
+++ b/bpf/progs/include/bpf_helpers.h
@@ -61,7 +61,6 @@
 struct kver_uint { unsigned int kver; };
 #define KVER_(v) ((struct kver_uint){ .kver = (v) })
 #define KVER(a, b, c) KVER_(((a) << 24) + ((b) << 16) + (c))
-#define KVER_NONE KVER_(0)
 #define KVER_4_9  KVER(4, 9, 0)
 #define KVER_4_14 KVER(4, 14, 0)
 #define KVER_4_19 KVER(4, 19, 0)
@@ -207,13 +206,13 @@
  */
 static void* (*bpf_map_lookup_elem_unsafe)(const void* map,
                                            const void* key) = (void*)BPF_FUNC_map_lookup_elem;
-static int (*bpf_map_update_elem_unsafe)(const void* map, const void* key,
-                                         const void* value, unsigned long long flags) = (void*)
+static long (*bpf_map_update_elem_unsafe)(const void* map, const void* key,
+                                          const void* value, unsigned long long flags) = (void*)
         BPF_FUNC_map_update_elem;
-static int (*bpf_map_delete_elem_unsafe)(const void* map,
-                                         const void* key) = (void*)BPF_FUNC_map_delete_elem;
-static int (*bpf_ringbuf_output_unsafe)(const void* ringbuf,
-                                        const void* data, __u64 size, __u64 flags) = (void*)
+static long (*bpf_map_delete_elem_unsafe)(const void* map,
+                                          const void* key) = (void*)BPF_FUNC_map_delete_elem;
+static long (*bpf_ringbuf_output_unsafe)(const void* ringbuf,
+                                         const void* data, __u64 size, __u64 flags) = (void*)
         BPF_FUNC_ringbuf_output;
 static void* (*bpf_ringbuf_reserve_unsafe)(const void* ringbuf,
                                            __u64 size, __u64 flags) = (void*)
@@ -223,8 +222,8 @@
 static void* (*bpf_sk_storage_get_unsafe) (const void* sk_storage, const void* sk,
                                            const void* value, unsigned long long flags) = (void*)
         BPF_FUNC_sk_storage_get;
-static int (*bpf_sk_storage_delete_unsafe) (const void* sk_storage,
-                                            const void* sk) = (void*) BPF_FUNC_sk_storage_delete;
+static long (*bpf_sk_storage_delete_unsafe) (const void* sk_storage,
+                                             const void* sk) = (void*) BPF_FUNC_sk_storage_delete;
 
 #define BPF_ANNOTATE_KV_PAIR(name, type_key, type_val)  \
         struct ____btf_map_##name {                     \
@@ -244,26 +243,26 @@
          type == BPF_MAP_TYPE_SK_STORAGE) ? BPF_F_NO_PREALLOC : 0) \
     )
 
-#define DEFINE_BPF_MAP_BASE(the_map, TYPE, keysize, valuesize, num_entries, usr, grp, md,      \
-                            selinux, pindir, minkver, maxkver, minloader, maxloader, mapflags) \
-    VALIDATE_SELINUX_CONTEXT(minloader, selinux);                                              \
-    VALIDATE_PIN_DIR(minloader, pindir);                                                       \
-    const struct bpf_map_def SECTION(".android_maps") the_map##_def = {                        \
-        .type = BPF_MAP_TYPE_##TYPE,                                                           \
-        .key_size = (keysize),                                                                 \
-        .value_size = (valuesize),                                                             \
-        .max_entries = ABSOLUTE(num_entries),                                                  \
-        .map_flags = DEFAULT_BPF_MAP_FLAGS(BPF_MAP_TYPE_##TYPE, num_entries, mapflags),        \
-        .uid = (usr),                                                                          \
-        .gid = (grp),                                                                          \
-        .mode = (md),                                                                          \
-        .bpfloader_min_ver = (minloader),                                                      \
-        .bpfloader_max_ver = (maxloader),                                                      \
-        .min_kver = (minkver).kver,                                                            \
-        .max_kver = (maxkver).kver,                                                            \
-        .create_location = CREATE_LOCATION(selinux),                                           \
-        .pin_location = "/sys/fs/bpf/" pindir "/map_" BPF_OBJ_NAME "_" #the_map,               \
-        .name_idx = __builtin_strlen("/sys/fs/bpf/" pindir "/map_" BPF_OBJ_NAME "_"),          \
+#define DEFINE_BPF_MAP_BASE(the_map, TYPE, keysize, valuesize, num_entries, usr, grp, md,       \
+                            selinux, pindir, minkver, maxkver, minloader, maxloader, mapflags)  \
+    VALIDATE_SELINUX_CONTEXT(minloader, selinux);                                               \
+    VALIDATE_PIN_DIR(minloader, pindir);                                                        \
+    const struct bpf_map_def SECTION(".android_maps") the_map##_def = {                         \
+        .type = BPF_MAP_TYPE_##TYPE,                                                            \
+        .key_size = (keysize),                                                                  \
+        .value_size = (valuesize),                                                              \
+        .max_entries = ABSOLUTE(num_entries),                                                   \
+        .map_flags = DEFAULT_BPF_MAP_FLAGS(BPF_MAP_TYPE_##TYPE, num_entries, mapflags),         \
+        .uid = (usr),                                                                           \
+        .gid = (grp),                                                                           \
+        .mode = (md),                                                                           \
+        .bpfloader_min_ver = (minloader),                                                       \
+        .bpfloader_max_ver = (maxloader),                                                       \
+        .min_kver = (minkver).kver,                                                             \
+        .max_kver = (maxkver).kver,                                                             \
+        .create_location = CREATE_LOCATION(selinux),                                            \
+        .pin_location = "/sys/fs/bpf/" pindir "/map_" BPF_OBJ_NAME "_" #the_map "\0",           \
+        .name_idx = __builtin_strlen("/sys/fs/bpf/" pindir "/map_" BPF_OBJ_NAME "_"),           \
     };
 
 #define __uint(name, val) int (*name)[val]
@@ -371,7 +370,7 @@
                            selinux, pindir, min_loader, max_loader, mapFlags)                    \
   DEFINE_BPF_MAP_BASE(the_map, TYPE, sizeof(KeyType), sizeof(ValueType),                         \
                       num_entries, usr, grp, md, selinux, pindir,                                \
-                      KVER_NONE, KVER_INF, min_loader, max_loader, mapFlags);                    \
+                      KVER_4_9, KVER_INF, min_loader, max_loader, mapFlags);                     \
     DEFINE_LIBBPF_MAP(the_map, TYPE, KeyType, ValueType, num_entries);                           \
     BPF_MAP_ASSERT_OK(BPF_MAP_TYPE_##TYPE, (num_entries), (md));                                 \
     _Static_assert(sizeof(KeyType) < 1024, "aosp/2370288 requires < 1024 byte keys");            \
@@ -459,10 +458,10 @@
 unsigned long long load_half(void* skb, unsigned long long off) asm("llvm.bpf.load.half");
 unsigned long long load_word(void* skb, unsigned long long off) asm("llvm.bpf.load.word");
 
-static int (*bpf_probe_read)(void* dst, int size, void* unsafe_ptr) = (void*) BPF_FUNC_probe_read;
-static int (*bpf_probe_read_str)(void* dst, int size, void* unsafe_ptr) = (void*) BPF_FUNC_probe_read_str;
-static int (*bpf_probe_read_user)(void* dst, int size, const void* unsafe_ptr) = (void*)BPF_FUNC_probe_read_user;
-static int (*bpf_probe_read_user_str)(void* dst, int size, const void* unsafe_ptr) = (void*) BPF_FUNC_probe_read_user_str;
+static long (*bpf_probe_read)(void* dst, int size, void* unsafe_ptr) = (void*) BPF_FUNC_probe_read;
+static long (*bpf_probe_read_str)(void* dst, int size, void* unsafe_ptr) = (void*) BPF_FUNC_probe_read_str;
+static long (*bpf_probe_read_user)(void* dst, int size, const void* unsafe_ptr) = (void*)BPF_FUNC_probe_read_user;
+static long (*bpf_probe_read_user_str)(void* dst, int size, const void* unsafe_ptr) = (void*) BPF_FUNC_probe_read_user_str;
 static unsigned long long (*bpf_ktime_get_ns)(void) = (void*) BPF_FUNC_ktime_get_ns;
 static unsigned long long (*bpf_ktime_get_boot_ns)(void) = (void*)BPF_FUNC_ktime_get_boot_ns;
 static unsigned long long (*bpf_get_current_pid_tgid)(void) = (void*) BPF_FUNC_get_current_pid_tgid;
@@ -474,33 +473,87 @@
 static struct bpf_sock* (*bpf_sk_fullsock)(struct bpf_sock* sk) = (void*) BPF_FUNC_sk_fullsock;
 
 // GPL only:
-static int (*bpf_trace_printk)(const char* fmt, int fmt_size, ...) = (void*) BPF_FUNC_trace_printk;
+static long (*bpf_trace_printk)(const char* fmt, int fmt_size, ...) = (void*) BPF_FUNC_trace_printk;
 #define bpf_printf(s, n...) bpf_trace_printk(s, sizeof(s), ## n)
 // Note: bpf only supports up to 3 arguments, log via: bpf_printf("msg %d %d %d", 1, 2, 3);
 // and read via the blocking: sudo cat /sys/kernel/debug/tracing/trace_pipe
 
-#define DEFINE_BPF_PROG_EXT(SECTION_NAME, prog_uid, prog_gid, the_prog, min_kv, max_kv,  \
-                            min_loader, max_loader, opt, selinux, pindir)                \
-    VALIDATE_SELINUX_CONTEXT(min_loader, selinux);                                       \
-    VALIDATE_PIN_DIR(min_loader, pindir);                                                \
-    const struct bpf_prog_def SECTION("progs") the_prog##_def = {                        \
-        .uid = (prog_uid),                                                               \
-        .gid = (prog_gid),                                                               \
-        .min_kver = (min_kv).kver,                                                       \
-        .max_kver = (max_kv).kver,                                                       \
-        .optional = (opt).optional,                                                      \
-        .bpfloader_min_ver = (min_loader),                                               \
-        .bpfloader_max_ver = (max_loader),                                               \
-        .create_location = CREATE_LOCATION(selinux),                                     \
-        .pin_prefix = "/sys/fs/bpf/" pindir "/prog_" BPF_OBJ_NAME "_",                   \
-    };                                                                                   \
-    SECTION(SECTION_NAME)                                                                \
-    int the_prog
+#define BPF_PROG_TYPE_bind4             BPF_PROG_TYPE_CGROUP_SOCK_ADDR
+#define BPF_PROG_TYPE_bind6             BPF_PROG_TYPE_CGROUP_SOCK_ADDR
+#define BPF_PROG_TYPE_cgroupskb         BPF_PROG_TYPE_CGROUP_SKB
+#define BPF_PROG_TYPE_cgroupsock        BPF_PROG_TYPE_CGROUP_SOCK
+#define BPF_PROG_TYPE_cgroupsockcreate  BPF_PROG_TYPE_CGROUP_SOCK
+#define BPF_PROG_TYPE_cgroupsockrelease BPF_PROG_TYPE_CGROUP_SOCK
+#define BPF_PROG_TYPE_connect4          BPF_PROG_TYPE_CGROUP_SOCK_ADDR
+#define BPF_PROG_TYPE_connect6          BPF_PROG_TYPE_CGROUP_SOCK_ADDR
+#define BPF_PROG_TYPE_egress            BPF_PROG_TYPE_CGROUP_SKB
+#define BPF_PROG_TYPE_getsockopt        BPF_PROG_TYPE_CGROUP_SOCKOPT
+#define BPF_PROG_TYPE_ingress           BPF_PROG_TYPE_CGROUP_SKB
+#define BPF_PROG_TYPE_postbind4         BPF_PROG_TYPE_CGROUP_SOCK
+#define BPF_PROG_TYPE_postbind6         BPF_PROG_TYPE_CGROUP_SOCK
+#define BPF_PROG_TYPE_recvmsg4          BPF_PROG_TYPE_CGROUP_SOCK_ADDR
+#define BPF_PROG_TYPE_recvmsg6          BPF_PROG_TYPE_CGROUP_SOCK_ADDR
+#define BPF_PROG_TYPE_schedact          BPF_PROG_TYPE_SCHED_ACT
+#define BPF_PROG_TYPE_schedcls          BPF_PROG_TYPE_SCHED_CLS
+#define BPF_PROG_TYPE_sendmsg4          BPF_PROG_TYPE_CGROUP_SOCK_ADDR
+#define BPF_PROG_TYPE_sendmsg6          BPF_PROG_TYPE_CGROUP_SOCK_ADDR
+#define BPF_PROG_TYPE_setsockopt        BPF_PROG_TYPE_CGROUP_SOCKOPT
+#define BPF_PROG_TYPE_skfilter          BPF_PROG_TYPE_SOCKET_FILTER
+#define BPF_PROG_TYPE_sockops           BPF_PROG_TYPE_SOCK_OPS
+#define BPF_PROG_TYPE_sysctl            BPF_PROG_TYPE_CGROUP_SYSCTL
+#define BPF_PROG_TYPE_xdp               BPF_PROG_TYPE_XDP
 
-#define DEFINE_BPF_PROG_KVER_RANGE_OPT(SECTION_NAME, prog_uid, prog_gid, the_prog, min_kv, max_kv, \
-                                       opt)                                                        \
-    DEFINE_BPF_PROG_EXT(SECTION_NAME, prog_uid, prog_gid, the_prog, min_kv, max_kv,                \
-                        BPFLOADER_MIN_VER, BPFLOADER_MAX_VER, opt,                                 \
+#define BPF_PROG_ATTACH_TYPE_DEFAULT BPF_CGROUP_INET_INGRESS
+
+#define BPF_PROG_ATTACH_TYPE_bind4             BPF_CGROUP_INET4_BIND
+#define BPF_PROG_ATTACH_TYPE_bind6             BPF_CGROUP_INET6_BIND
+#define BPF_PROG_ATTACH_TYPE_cgroupskb         BPF_PROG_ATTACH_TYPE_DEFAULT
+#define BPF_PROG_ATTACH_TYPE_cgroupsock        BPF_PROG_ATTACH_TYPE_DEFAULT
+#define BPF_PROG_ATTACH_TYPE_cgroupsockcreate  BPF_CGROUP_INET_SOCK_CREATE
+#define BPF_PROG_ATTACH_TYPE_cgroupsockrelease BPF_CGROUP_INET_SOCK_RELEASE
+#define BPF_PROG_ATTACH_TYPE_connect4          BPF_CGROUP_INET4_CONNECT
+#define BPF_PROG_ATTACH_TYPE_connect6          BPF_CGROUP_INET6_CONNECT
+#define BPF_PROG_ATTACH_TYPE_egress            BPF_CGROUP_INET_EGRESS
+#define BPF_PROG_ATTACH_TYPE_getsockopt        BPF_CGROUP_GETSOCKOPT
+#define BPF_PROG_ATTACH_TYPE_ingress           BPF_CGROUP_INET_INGRESS
+#define BPF_PROG_ATTACH_TYPE_postbind4         BPF_CGROUP_INET4_POST_BIND
+#define BPF_PROG_ATTACH_TYPE_postbind6         BPF_CGROUP_INET6_POST_BIND
+#define BPF_PROG_ATTACH_TYPE_recvmsg4          BPF_CGROUP_UDP4_RECVMSG
+#define BPF_PROG_ATTACH_TYPE_recvmsg6          BPF_CGROUP_UDP6_RECVMSG
+#define BPF_PROG_ATTACH_TYPE_schedact          BPF_PROG_ATTACH_TYPE_DEFAULT
+#define BPF_PROG_ATTACH_TYPE_schedcls          BPF_PROG_ATTACH_TYPE_DEFAULT
+#define BPF_PROG_ATTACH_TYPE_sendmsg4          BPF_CGROUP_UDP4_SENDMSG
+#define BPF_PROG_ATTACH_TYPE_sendmsg6          BPF_CGROUP_UDP6_SENDMSG
+#define BPF_PROG_ATTACH_TYPE_setsockopt        BPF_CGROUP_SETSOCKOPT
+#define BPF_PROG_ATTACH_TYPE_skfilter          BPF_PROG_ATTACH_TYPE_DEFAULT
+#define BPF_PROG_ATTACH_TYPE_sockops           BPF_CGROUP_SOCK_OPS
+#define BPF_PROG_ATTACH_TYPE_sysctl            BPF_CGROUP_SYSCTL
+#define BPF_PROG_ATTACH_TYPE_xdp               BPF_PROG_ATTACH_TYPE_DEFAULT
+
+#define DEFINE_BPF_PROG_EXT(TYPE, NAME, VER, prog_uid, prog_gid, min_kv, max_kv,              \
+                            min_loader, max_loader, opt, selinux, pindir)                     \
+    VALIDATE_SELINUX_CONTEXT(min_loader, selinux);                                            \
+    VALIDATE_PIN_DIR(min_loader, pindir);                                                     \
+    const struct bpf_prog_def SECTION(".android_progs") TYPE##_##NAME##_##VER##_def = {       \
+        .type = BPF_PROG_TYPE_##TYPE,                                                         \
+        .attach_type = BPF_PROG_ATTACH_TYPE_##TYPE,                                           \
+        .uid = (prog_uid),                                                                    \
+        .gid = (prog_gid),                                                                    \
+        .min_kver = (KVER_##min_kv).kver,                                                     \
+        .max_kver = (KVER_##max_kv).kver,                                                     \
+        .optional = (opt).optional,                                                           \
+        .bpfloader_min_ver = (min_loader),                                                    \
+        .bpfloader_max_ver = (max_loader),                                                    \
+        .create_location = CREATE_LOCATION(selinux),                                          \
+        .pin_location = "/sys/fs/bpf/" pindir "/prog_" BPF_OBJ_NAME "_" #TYPE "_" #NAME "\0", \
+        .name_idx = __builtin_strlen("/sys/fs/bpf/" pindir "/prog_" BPF_OBJ_NAME "_"),        \
+    };                                                                                        \
+    SECTION(#TYPE "/" #NAME "$" #VER)                                                         \
+    long TYPE##_##NAME##_##VER
+
+#define DEFINE_BPF_PROG_KVER_RANGE_OPT(TYPE, NAME, VER, prog_gid, min_kv, max_kv, opt) \
+    DEFINE_BPF_PROG_EXT(TYPE, NAME, VER, AID_ROOT, prog_gid, min_kv, max_kv,           \
+                        BPFLOADER_MIN_VER, BPFLOADER_MAX_VER, opt,                     \
                         DEFAULT_BPF_MAP_SELINUX_CONTEXT, DEFAULT_BPF_PIN_SUBDIR)
 
 // Programs (here used in the sense of functions/sections) marked optional are allowed to fail
@@ -515,26 +568,19 @@
 // ie. a non-optional program in a critical .o is mandatory for kernels matching the min/max kver.
 
 // programs requiring a kernel version >= min_kv && < max_kv
-#define DEFINE_BPF_PROG_KVER_RANGE(SECTION_NAME, prog_uid, prog_gid, the_prog, min_kv, max_kv) \
-    DEFINE_BPF_PROG_KVER_RANGE_OPT(SECTION_NAME, prog_uid, prog_gid, the_prog, min_kv, max_kv, \
-                                   MANDATORY)
-#define DEFINE_OPTIONAL_BPF_PROG_KVER_RANGE(SECTION_NAME, prog_uid, prog_gid, the_prog, min_kv, \
-                                            max_kv)                                             \
-    DEFINE_BPF_PROG_KVER_RANGE_OPT(SECTION_NAME, prog_uid, prog_gid, the_prog, min_kv, max_kv, \
-                                   OPTIONAL)
+#define DEFINE_BPF_PROG_KVER_RANGE(TYPE, NAME, VER, prog_gid, min_kv, max_kv) \
+    DEFINE_BPF_PROG_KVER_RANGE_OPT(TYPE, NAME, VER, prog_gid, min_kv, max_kv, MANDATORY)
+#define DEFINE_OPTIONAL_BPF_PROG_KVER_RANGE(TYPE, NAME, VER, prog_gid, min_kv, max_kv)  \
+    DEFINE_BPF_PROG_KVER_RANGE_OPT(TYPE, NAME, VER, prog_gid, min_kv, max_kv, OPTIONAL)
 
 // programs requiring a kernel version >= min_kv
-#define DEFINE_BPF_PROG_KVER(SECTION_NAME, prog_uid, prog_gid, the_prog, min_kv)                 \
-    DEFINE_BPF_PROG_KVER_RANGE_OPT(SECTION_NAME, prog_uid, prog_gid, the_prog, min_kv, KVER_INF, \
-                                   MANDATORY)
-#define DEFINE_OPTIONAL_BPF_PROG_KVER(SECTION_NAME, prog_uid, prog_gid, the_prog, min_kv)        \
-    DEFINE_BPF_PROG_KVER_RANGE_OPT(SECTION_NAME, prog_uid, prog_gid, the_prog, min_kv, KVER_INF, \
-                                   OPTIONAL)
+#define DEFINE_BPF_PROG_KVER(TYPE, NAME, VER, prog_gid, min_kv)                 \
+    DEFINE_BPF_PROG_KVER_RANGE_OPT(TYPE, NAME, VER, prog_gid, min_kv, INF, MANDATORY)
+#define DEFINE_OPTIONAL_BPF_PROG_KVER(TYPE, NAME, VER, prog_gid, min_kv)        \
+    DEFINE_BPF_PROG_KVER_RANGE_OPT(TYPE, NAME, VER, prog_gid, min_kv, INF, OPTIONAL)
 
 // programs with no kernel version requirements
-#define DEFINE_BPF_PROG(SECTION_NAME, prog_uid, prog_gid, the_prog) \
-    DEFINE_BPF_PROG_KVER_RANGE_OPT(SECTION_NAME, prog_uid, prog_gid, the_prog, KVER_NONE, KVER_INF, \
-                                   MANDATORY)
-#define DEFINE_OPTIONAL_BPF_PROG(SECTION_NAME, prog_uid, prog_gid, the_prog) \
-    DEFINE_BPF_PROG_KVER_RANGE_OPT(SECTION_NAME, prog_uid, prog_gid, the_prog, KVER_NONE, KVER_INF, \
-                                   OPTIONAL)
+#define DEFINE_BPF_PROG(TYPE, NAME, VER, prog_gid) \
+    DEFINE_BPF_PROG_KVER_RANGE_OPT(TYPE, NAME, VER, prog_gid, 4_9, INF, MANDATORY)
+#define DEFINE_OPTIONAL_BPF_PROG(TYPE, NAME, VER, prog_gid) \
+    DEFINE_BPF_PROG_KVER_RANGE_OPT(TYPE, NAME, VER, prog_gid, 4_9, INF, OPTIONAL)
diff --git a/bpf/progs/include/bpf_map_def.h b/bpf/progs/include/bpf_map_def.h
index 388d5ef..63c31ac 100644
--- a/bpf/progs/include/bpf_map_def.h
+++ b/bpf/progs/include/bpf_map_def.h
@@ -109,7 +109,7 @@
 
 // Length of strings (incl. selinux_context and pin_subdir)
 // in the bpf_map_def and bpf_prog_def structs.
-#define BPF_DEF_CHAR_ARRAY_SIZE 66  // must be even for alignment sanity
+#define BPF_DEF_CHAR_ARRAY_SIZE 70  // must be even for alignment sanity
 
 /*
  * Map structure to be used by Android eBPF C programs. The Android eBPF loader
@@ -167,6 +167,9 @@
 _Static_assert(_Alignof(struct bpf_map_def) == 4, "_Alignof struct bpf_map_def != 4");
 
 struct bpf_prog_def {
+    enum bpf_prog_type type;
+    enum bpf_attach_type attach_type;
+
     unsigned int uid;
     unsigned int gid;
 
@@ -182,11 +185,21 @@
     unsigned int bpfloader_max_ver;
 
     char create_location[BPF_DEF_CHAR_ARRAY_SIZE];
-    char pin_prefix[BPF_DEF_CHAR_ARRAY_SIZE];
+    char pin_location[BPF_DEF_CHAR_ARRAY_SIZE];
+    unsigned int name_idx;
+
+#ifdef __cplusplus
+    const char * name() const { return this->pin_location + this->name_idx; }
+#endif
 };
 
+#ifdef __cplusplus
+static_assert(std::is_pod_v<struct bpf_prog_def>);
+static_assert(std::is_standard_layout_v<struct bpf_prog_def>);
+#endif
+
 // This needs to be updated whenever the above structure definition is expanded.
 // These asserts are here to make sure we have cross-6-arch consistency.
-_Static_assert(sizeof(struct bpf_prog_def) == 28 + 2 * BPF_DEF_CHAR_ARRAY_SIZE, "wrong sizeof struct bpf_prog_def");
+_Static_assert(sizeof(struct bpf_prog_def) == 40 + 2 * BPF_DEF_CHAR_ARRAY_SIZE, "wrong sizeof struct bpf_prog_def");
 _Static_assert(__alignof__(struct bpf_prog_def) == 4, "__alignof__ struct bpf_prog_def != 4");
 _Static_assert(_Alignof(struct bpf_prog_def) == 4, "_Alignof struct bpf_prog_def != 4");
diff --git a/bpf/progs/netd.c b/bpf/progs/netd.c
index 7cb9905..f83a306 100644
--- a/bpf/progs/netd.c
+++ b/bpf/progs/netd.c
@@ -113,35 +113,35 @@
 // program (see XT_BPF_MODE_PATH_PINNED) and then the iptables binary (or rather
 // the kernel acting on behalf of it) must be able to retrieve the pinned program
 // for the reload to succeed
-#define DEFINE_XTBPF_PROG(SECTION_NAME, the_prog) \
-    DEFINE_BPF_PROG(SECTION_NAME, AID_ROOT, AID_NET_ADMIN, the_prog)
+#define DEFINE_XTBPF_PROG(TYPE, NAME, VER) \
+    DEFINE_BPF_PROG(TYPE, NAME, VER, AID_NET_ADMIN)
 
 // programs that need to be usable by netd, but not by netutils_wrappers
 // (this is because these are currently attached by the mainline provided libnetd_updatable .so
 // which is loaded into netd and thus runs as netd uid/gid/selinux context)
-#define DEFINE_NETD_BPF_PROG_RANGES(SECTION_NAME, the_prog, minKV, maxKV, min_loader, max_loader) \
-    DEFINE_BPF_PROG_EXT(SECTION_NAME, AID_ROOT, AID_ROOT, the_prog,                               \
-                        minKV, maxKV, min_loader, max_loader, MANDATORY,                          \
+#define DEFINE_NETD_BPF_PROG_RANGES(TYPE, NAME, VER, minKV, maxKV, min_loader, max_loader) \
+    DEFINE_BPF_PROG_EXT(TYPE, NAME, VER, AID_ROOT, AID_ROOT,                               \
+                        minKV, maxKV, min_loader, max_loader, MANDATORY,                   \
                         "netd_readonly", DEFAULT_BPF_PIN_SUBDIR)
 
-#define DEFINE_NETD_BPF_PROG_KVER_RANGE(SECTION_NAME, the_prog, minKV, maxKV) \
-    DEFINE_NETD_BPF_PROG_RANGES(SECTION_NAME, the_prog, minKV, maxKV, BPFLOADER_MIN_VER, BPFLOADER_MAX_VER)
+#define DEFINE_NETD_BPF_PROG_KVER_RANGE(TYPE, NAME, VER, minKV, maxKV) \
+    DEFINE_NETD_BPF_PROG_RANGES(TYPE, NAME, VER, minKV, maxKV, BPFLOADER_MIN_VER, BPFLOADER_MAX_VER)
 
-#define DEFINE_NETD_BPF_PROG_KVER(SECTION_NAME, the_prog, min_kv) \
-    DEFINE_NETD_BPF_PROG_KVER_RANGE(SECTION_NAME, the_prog, min_kv, KVER_INF)
+#define DEFINE_NETD_BPF_PROG_KVER(TYPE, NAME, VER, min_kv) \
+    DEFINE_NETD_BPF_PROG_KVER_RANGE(TYPE, NAME, VER, min_kv, INF)
 
-#define DEFINE_NETD_BPF_PROG(SECTION_NAME, the_prog) \
-    DEFINE_NETD_BPF_PROG_KVER(SECTION_NAME, the_prog, KVER_NONE)
+#define DEFINE_NETD_BPF_PROG(TYPE, NAME, VER) \
+    DEFINE_NETD_BPF_PROG_KVER(TYPE, NAME, VER, 4_9)
 
-#define DEFINE_NETD_V_BPF_PROG_KVER(SECTION_NAME, the_prog, minKV)                                \
-    DEFINE_BPF_PROG_EXT(SECTION_NAME, AID_ROOT, AID_ROOT, the_prog, minKV,                        \
-                        KVER_INF, BPFLOADER_MAINLINE_V_VERSION, BPFLOADER_MAX_VER, MANDATORY,     \
+#define DEFINE_NETD_V_BPF_PROG_KVER(TYPE, NAME, VER, minKV)                         \
+    DEFINE_BPF_PROG_EXT(TYPE, NAME, VER, AID_ROOT, AID_ROOT, minKV, INF,            \
+                        BPFLOADER_MAINLINE_V_VERSION, BPFLOADER_MAX_VER, MANDATORY, \
                         "netd_readonly", DEFAULT_BPF_PIN_SUBDIR)
 
 // programs that only need to be usable by the system server
-#define DEFINE_SYS_BPF_PROG(SECTION_NAME, the_prog) \
-    DEFINE_BPF_PROG_EXT(SECTION_NAME, AID_ROOT, AID_NET_ADMIN, the_prog, KVER_NONE, KVER_INF,  \
-                        BPFLOADER_MIN_VER, BPFLOADER_MAX_VER, MANDATORY, \
+#define DEFINE_SYS_BPF_PROG(TYPE, NAME, VER) \
+    DEFINE_BPF_PROG_EXT(TYPE, NAME, VER, AID_ROOT, AID_NET_ADMIN, 4_9, INF, \
+                        BPFLOADER_MIN_VER, BPFLOADER_MAX_VER, MANDATORY,    \
                         "net_shared", DEFAULT_BPF_PIN_SUBDIR)
 
 /*
@@ -608,101 +608,89 @@
 // ----- cgroupskb/ingress/stats -----
 
 // Android 25Q2+ 5.10+ (localnet protection + tracing)
-DEFINE_NETD_BPF_PROG_RANGES("cgroupskb/ingress/stats$5_10_25q2",
-                            bpf_cgroup_ingress_5_10_25q2, KVER_5_10, KVER_INF,
+DEFINE_NETD_BPF_PROG_RANGES(cgroupskb, ingress_stats, 5_10_25q2, 5_10, INF,
                             BPFLOADER_MAINLINE_25Q2_VERSION, BPFLOADER_MAX_VER)
 (struct __sk_buff* skb) {
     return bpf_traffic_account(skb, INGRESS, KVER_5_10, SDK_LEVEL_25Q2);
 }
 
 // Android 25Q2+ 5.4 (localnet protection)
-DEFINE_NETD_BPF_PROG_RANGES("cgroupskb/ingress/stats$5_4_25q2",
-                            bpf_cgroup_ingress_5_4_25q2, KVER_5_4, KVER_5_10,
+DEFINE_NETD_BPF_PROG_RANGES(cgroupskb, ingress_stats, 5_4_25q2, 5_4, 5_10,
                             BPFLOADER_MAINLINE_25Q2_VERSION, BPFLOADER_MAX_VER)
 (struct __sk_buff* skb) {
     return bpf_traffic_account(skb, INGRESS, KVER_5_4, SDK_LEVEL_25Q2);
 }
 
 // Android U/V 5.10+ (tracing)
-DEFINE_NETD_BPF_PROG_RANGES("cgroupskb/ingress/stats$5_10_u",
-                            bpf_cgroup_ingress_5_10_u, KVER_5_10, KVER_INF,
+DEFINE_NETD_BPF_PROG_RANGES(cgroupskb, ingress_stats, 5_10_u, 5_10, INF,
                             BPFLOADER_MAINLINE_U_VERSION, BPFLOADER_MAINLINE_25Q2_VERSION)
 (struct __sk_buff* skb) {
     return bpf_traffic_account(skb, INGRESS, KVER_5_10, SDK_LEVEL_U);
 }
 
 // Android T/U/V/25Q2 5.4 & T 5.10/5.15
-DEFINE_NETD_BPF_PROG_KVER_RANGE("cgroupskb/ingress/stats$5_4",
-                                bpf_cgroup_ingress_5_4, KVER_5_4, KVER_INF)
+DEFINE_NETD_BPF_PROG_KVER_RANGE(cgroupskb, ingress_stats, 5_4, 5_4, INF)
 (struct __sk_buff* skb) {
     return bpf_traffic_account(skb, INGRESS, KVER_5_4, SDK_LEVEL_T);
 }
 
 // Android T/U/V 4.19
-DEFINE_NETD_BPF_PROG_KVER_RANGE("cgroupskb/ingress/stats$4_19",
-                               bpf_cgroup_ingress_4_19, KVER_4_19, KVER_5_4)
+DEFINE_NETD_BPF_PROG_KVER_RANGE(cgroupskb, ingress_stats, 4_19, 4_19, 5_4)
 (struct __sk_buff* skb) {
 return bpf_traffic_account(skb, INGRESS, KVER_4_19, SDK_LEVEL_T);
 }
 
 // Android T 4.9 & T/U 4.14
-DEFINE_NETD_BPF_PROG_KVER_RANGE("cgroupskb/ingress/stats$4_9",
-                                bpf_cgroup_ingress_4_9, KVER_NONE, KVER_4_19)
+DEFINE_NETD_BPF_PROG_KVER_RANGE(cgroupskb, ingress_stats, 4_9, 4_9, 4_19)
 (struct __sk_buff* skb) {
-    return bpf_traffic_account(skb, INGRESS, KVER_NONE, SDK_LEVEL_T);
+    return bpf_traffic_account(skb, INGRESS, KVER_4_9, SDK_LEVEL_T);
 }
 
 // ----- cgroupskb/egress/stats -----
 
 // Android 25Q2+ 5.10+ (localnet protection + tracing)
-DEFINE_NETD_BPF_PROG_RANGES("cgroupskb/egress/stats$5_10_25q2",
-                            bpf_cgroup_egress_5_10_25q2, KVER_5_10, KVER_INF,
+DEFINE_NETD_BPF_PROG_RANGES(cgroupskb, egress_stats, 5_10_25q2, 5_10, INF,
                             BPFLOADER_MAINLINE_25Q2_VERSION, BPFLOADER_MAX_VER)
 (struct __sk_buff* skb) {
     return bpf_traffic_account(skb, EGRESS, KVER_5_10, SDK_LEVEL_25Q2);
 }
 
 // Android 25Q2+ 5.4 (localnet protection)
-DEFINE_NETD_BPF_PROG_RANGES("cgroupskb/egress/stats$5_4_25q2",
-                            bpf_cgroup_egress_5_4_25q2, KVER_5_4, KVER_5_10,
+DEFINE_NETD_BPF_PROG_RANGES(cgroupskb, egress_stats, 5_4_25q2, 5_4, 5_10,
                             BPFLOADER_MAINLINE_25Q2_VERSION, BPFLOADER_MAX_VER)
 (struct __sk_buff* skb) {
     return bpf_traffic_account(skb, EGRESS, KVER_5_4, SDK_LEVEL_25Q2);
 }
 
 // Android U/V 5.10+ (tracing)
-DEFINE_NETD_BPF_PROG_RANGES("cgroupskb/egress/stats$5_10_u",
-                            bpf_cgroup_egress_5_10_u, KVER_5_10, KVER_INF,
+DEFINE_NETD_BPF_PROG_RANGES(cgroupskb, egress_stats, 5_10_u, 5_10, INF,
                             BPFLOADER_MAINLINE_U_VERSION, BPFLOADER_MAINLINE_25Q2_VERSION)
 (struct __sk_buff* skb) {
     return bpf_traffic_account(skb, EGRESS, KVER_5_10, SDK_LEVEL_U);
 }
 
 // Android T/U/V/25Q2 5.4 & T 5.10/5.15
-DEFINE_NETD_BPF_PROG_KVER_RANGE("cgroupskb/egress/stats$5_4",
-                                bpf_cgroup_egress_5_4, KVER_5_4, KVER_INF)
+DEFINE_NETD_BPF_PROG_KVER_RANGE(cgroupskb, egress_stats, 5_4, 5_4, INF)
 (struct __sk_buff* skb) {
     return bpf_traffic_account(skb, EGRESS, KVER_5_4, SDK_LEVEL_T);
 }
 
 // Android T/U/V 4.19
-DEFINE_NETD_BPF_PROG_KVER_RANGE("cgroupskb/egress/stats$4_19",
-                                bpf_cgroup_egress_4_19, KVER_4_19, KVER_5_4)
+DEFINE_NETD_BPF_PROG_KVER_RANGE(cgroupskb, egress_stats, 4_19, 4_19, 5_4)
 (struct __sk_buff* skb) {
 return bpf_traffic_account(skb, EGRESS, KVER_4_19, SDK_LEVEL_T);
 }
 
 // Android T 4.9 & T/U 4.14
-DEFINE_NETD_BPF_PROG_KVER_RANGE("cgroupskb/egress/stats$4_9",
-                                bpf_cgroup_egress_4_9, KVER_NONE, KVER_4_19)
+DEFINE_NETD_BPF_PROG_KVER_RANGE(cgroupskb, egress_stats, 4_9, 4_9, 4_19)
 (struct __sk_buff* skb) {
-    return bpf_traffic_account(skb, EGRESS, KVER_NONE, SDK_LEVEL_T);
+    return bpf_traffic_account(skb, EGRESS, KVER_4_9, SDK_LEVEL_T);
 }
 
 // -----
 
 // WARNING: Android T's non-updatable netd depends on the name of this program.
-DEFINE_XTBPF_PROG("skfilter/egress/xtbpf", xt_bpf_egress_prog)
+DEFINE_XTBPF_PROG(skfilter, egress_xtbpf, )
 (struct __sk_buff* skb) {
     // Clat daemon does not generate new traffic, all its traffic is accounted for already
     // on the v4-* interfaces (except for the 20 (or 28) extra bytes of IPv6 vs IPv4 overhead,
@@ -716,12 +704,12 @@
     }
 
     uint32_t key = skb->ifindex;
-    update_iface_stats_map(skb, &key, EGRESS, KVER_NONE);
+    update_iface_stats_map(skb, &key, EGRESS, KVER_4_9);
     return XTBPF_MATCH;
 }
 
 // WARNING: Android T's non-updatable netd depends on the name of this program.
-DEFINE_XTBPF_PROG("skfilter/ingress/xtbpf", xt_bpf_ingress_prog)
+DEFINE_XTBPF_PROG(skfilter, ingress_xtbpf, )
 (struct __sk_buff* skb) {
     // Clat daemon traffic is not accounted by virtue of iptables raw prerouting drop rule
     // (in clat_raw_PREROUTING chain), which triggers before this (in bw_raw_PREROUTING chain).
@@ -729,23 +717,22 @@
     // Keep that in mind when moving this out of iptables xt_bpf and into tc ingress (or xdp).
 
     uint32_t key = skb->ifindex;
-    update_iface_stats_map(skb, &key, INGRESS, KVER_NONE);
+    update_iface_stats_map(skb, &key, INGRESS, KVER_4_9);
     return XTBPF_MATCH;
 }
 
-DEFINE_SYS_BPF_PROG("schedact/ingress/account",
-                    tc_bpf_ingress_account_prog)
+DEFINE_SYS_BPF_PROG(schedact, ingress_account, )
 (struct __sk_buff* skb) {
     if (is_received_skb(skb)) {
         // Account for ingress traffic before tc drops it.
         uint32_t key = skb->ifindex;
-        update_iface_stats_map(skb, &key, INGRESS, KVER_NONE);
+        update_iface_stats_map(skb, &key, INGRESS, KVER_4_9);
     }
     return TC_ACT_UNSPEC;
 }
 
 // WARNING: Android T's non-updatable netd depends on the name of this program.
-DEFINE_XTBPF_PROG("skfilter/allowlist/xtbpf", xt_bpf_allowlist_prog)
+DEFINE_XTBPF_PROG(skfilter, allowlist_xtbpf, )
 (struct __sk_buff* skb) {
     uint32_t sock_uid = bpf_get_socket_uid(skb);
     if (is_system_uid(sock_uid)) return XTBPF_MATCH;
@@ -764,7 +751,7 @@
 }
 
 // WARNING: Android T's non-updatable netd depends on the name of this program.
-DEFINE_XTBPF_PROG("skfilter/denylist/xtbpf", xt_bpf_denylist_prog)
+DEFINE_XTBPF_PROG(skfilter, denylist_xtbpf, )
 (struct __sk_buff* skb) {
     uint32_t sock_uid = bpf_get_socket_uid(skb);
     UidOwnerValue* denylistMatch = bpf_uid_owner_map_lookup_elem(&sock_uid);
@@ -796,18 +783,17 @@
     return (get_app_permissions() & BPF_PERMISSION_INTERNET) ? BPF_ALLOW : BPF_DISALLOW;
 }
 
-DEFINE_NETD_BPF_PROG_KVER("cgroupsock/inet_create$5_10", inet_socket_create_5_10, KVER_5_10)
+DEFINE_NETD_BPF_PROG_KVER(cgroupsock, inet_create, 5_10, 5_10)
 (struct bpf_sock* sk) {
     return inet_socket_create(sk, KVER_5_10);
 }
 
-DEFINE_NETD_BPF_PROG_KVER_RANGE("cgroupsock/inet_create$4_14",
-                                inet_socket_create_4_14, KVER_4_14, KVER_5_10)
+DEFINE_NETD_BPF_PROG_KVER_RANGE(cgroupsock, inet_create, 4_14, 4_14, 5_10)
 (struct bpf_sock* sk) {
     return inet_socket_create(sk, KVER_4_14);
 }
 
-DEFINE_NETD_BPF_PROG_KVER("cgroupsockrelease/inet_release", inet_socket_release, KVER_5_10)
+DEFINE_NETD_BPF_PROG_KVER(cgroupsockrelease, inet_release, , 5_10)
 (struct bpf_sock* sk) {
     uint64_t cookie = bpf_get_sk_cookie(sk);
     if (cookie) bpf_cookie_tag_map_delete_elem(&cookie);
@@ -860,47 +846,47 @@
     return BPF_ALLOW;
 }
 
-DEFINE_NETD_BPF_PROG_KVER("bind4/inet4_bind", inet4_bind, KVER_4_19)
+DEFINE_NETD_BPF_PROG_KVER(bind4, inet4_bind, , 4_19)
 (struct bpf_sock_addr *ctx) {
     return block_port(ctx);
 }
 
-DEFINE_NETD_BPF_PROG_KVER("bind6/inet6_bind", inet6_bind, KVER_4_19)
+DEFINE_NETD_BPF_PROG_KVER(bind6, inet6_bind, , 4_19)
 (struct bpf_sock_addr *ctx) {
     return block_port(ctx);
 }
 
-DEFINE_NETD_V_BPF_PROG_KVER("connect4/inet4_connect", inet4_connect, KVER_4_19)
+DEFINE_NETD_V_BPF_PROG_KVER(connect4, inet4_connect, , 4_19)
 (struct bpf_sock_addr *ctx) {
     return check_localhost(ctx);
 }
 
-DEFINE_NETD_V_BPF_PROG_KVER("connect6/inet6_connect", inet6_connect, KVER_4_19)
+DEFINE_NETD_V_BPF_PROG_KVER(connect6, inet6_connect, , 4_19)
 (struct bpf_sock_addr *ctx) {
     return check_localhost(ctx);
 }
 
-DEFINE_NETD_V_BPF_PROG_KVER("recvmsg4/udp4_recvmsg", udp4_recvmsg, KVER_4_19)
+DEFINE_NETD_V_BPF_PROG_KVER(recvmsg4, udp4_recvmsg, , 4_19)
 (struct bpf_sock_addr *ctx) {
     return check_localhost(ctx);
 }
 
-DEFINE_NETD_V_BPF_PROG_KVER("recvmsg6/udp6_recvmsg", udp6_recvmsg, KVER_4_19)
+DEFINE_NETD_V_BPF_PROG_KVER(recvmsg6, udp6_recvmsg, , 4_19)
 (struct bpf_sock_addr *ctx) {
     return check_localhost(ctx);
 }
 
-DEFINE_NETD_V_BPF_PROG_KVER("sendmsg4/udp4_sendmsg", udp4_sendmsg, KVER_4_19)
+DEFINE_NETD_V_BPF_PROG_KVER(sendmsg4, udp4_sendmsg, , 4_19)
 (struct bpf_sock_addr *ctx) {
     return check_localhost(ctx);
 }
 
-DEFINE_NETD_V_BPF_PROG_KVER("sendmsg6/udp6_sendmsg", udp6_sendmsg, KVER_4_19)
+DEFINE_NETD_V_BPF_PROG_KVER(sendmsg6, udp6_sendmsg, , 4_19)
 (struct bpf_sock_addr *ctx) {
     return check_localhost(ctx);
 }
 
-DEFINE_NETD_V_BPF_PROG_KVER("getsockopt/prog", getsockopt_prog, KVER_5_4)
+DEFINE_NETD_V_BPF_PROG_KVER(getsockopt, prog, , 5_4)
 (struct bpf_sockopt *ctx) {
     // Tell kernel to return 'original' kernel reply (instead of the bpf modified buffer)
     // This is important if the answer is larger than PAGE_SIZE (max size this bpf hook can provide)
@@ -908,7 +894,7 @@
     return BPF_ALLOW;
 }
 
-DEFINE_NETD_V_BPF_PROG_KVER("setsockopt/prog", setsockopt_prog, KVER_5_4)
+DEFINE_NETD_V_BPF_PROG_KVER(setsockopt, prog, , 5_4)
 (struct bpf_sockopt *ctx) {
     // Tell kernel to use/process original buffer provided by userspace.
     // This is important if it is larger than PAGE_SIZE (max size this bpf hook can handle).
diff --git a/bpf/progs/offload.c b/bpf/progs/offload.c
index 0401d70..4e82d01 100644
--- a/bpf/progs/offload.c
+++ b/bpf/progs/offload.c
@@ -257,29 +257,25 @@
 }
 
 // implementation for 5.4+ (can use skb->gso_{segs,size})
-DEFINE_BPF_PROG_KVER("schedcls/tether_downstream6_ether$5_4", AID_ROOT, AID_NETWORK_STACK,
-                     sched_cls_tether_downstream6_ether_5_4, KVER_5_4)
+DEFINE_BPF_PROG_KVER(schedcls, tether_downstream6_ether, 5_4, AID_NETWORK_STACK, 5_4)
 (struct __sk_buff* skb) {
     return do_forward6(skb, ETHER, DOWNSTREAM, KVER_5_4);
 }
 
-DEFINE_BPF_PROG_KVER("schedcls/tether_upstream6_ether$5_4", AID_ROOT, AID_NETWORK_STACK,
-                     sched_cls_tether_upstream6_ether_5_4, KVER_5_4)
+DEFINE_BPF_PROG_KVER(schedcls, tether_upstream6_ether, 5_4, AID_NETWORK_STACK, 5_4)
 (struct __sk_buff* skb) {
     return do_forward6(skb, ETHER, UPSTREAM, KVER_5_4);
 }
 
 // implementation for 4.9/4.14/4.19
-DEFINE_BPF_PROG_KVER_RANGE("schedcls/tether_downstream6_ether", AID_ROOT, AID_NETWORK_STACK,
-                           sched_cls_tether_downstream6_ether, KVER_NONE, KVER_5_4)
+DEFINE_BPF_PROG_KVER_RANGE(schedcls, tether_downstream6_ether, , AID_NETWORK_STACK, 4_9, 5_4)
 (struct __sk_buff* skb) {
-    return do_forward6(skb, ETHER, DOWNSTREAM, KVER_NONE);
+    return do_forward6(skb, ETHER, DOWNSTREAM, KVER_4_9);
 }
 
-DEFINE_BPF_PROG_KVER_RANGE("schedcls/tether_upstream6_ether", AID_ROOT, AID_NETWORK_STACK,
-                           sched_cls_tether_upstream6_ether, KVER_NONE, KVER_5_4)
+DEFINE_BPF_PROG_KVER_RANGE(schedcls, tether_upstream6_ether, , AID_NETWORK_STACK, 4_9, 5_4)
 (struct __sk_buff* skb) {
-    return do_forward6(skb, ETHER, UPSTREAM, KVER_NONE);
+    return do_forward6(skb, ETHER, UPSTREAM, KVER_4_9);
 }
 
 // Note: section names must be unique to prevent programs from appending to each other,
@@ -296,40 +292,34 @@
 // and in system/netd/tests/binder_test.cpp NetdBinderTest TetherOffloadForwarding.
 //
 // Hence, these mandatory (must load successfully) implementations for 5.4+ kernels:
-DEFINE_BPF_PROG_KVER("schedcls/tether_downstream6_rawip$5_4", AID_ROOT, AID_NETWORK_STACK,
-                     sched_cls_tether_downstream6_rawip_5_4, KVER_5_4)
+DEFINE_BPF_PROG_KVER(schedcls, tether_downstream6_rawip, 5_4, AID_NETWORK_STACK, 5_4)
 (struct __sk_buff* skb) {
     return do_forward6(skb, RAWIP, DOWNSTREAM, KVER_5_4);
 }
 
-DEFINE_BPF_PROG_KVER("schedcls/tether_upstream6_rawip$5_4", AID_ROOT, AID_NETWORK_STACK,
-                     sched_cls_tether_upstream6_rawip_5_4, KVER_5_4)
+DEFINE_BPF_PROG_KVER(schedcls, tether_upstream6_rawip, 5_4, AID_NETWORK_STACK, 5_4)
 (struct __sk_buff* skb) {
     return do_forward6(skb, RAWIP, UPSTREAM, KVER_5_4);
 }
 
 // and for 4.14/4.19 kernels
-DEFINE_BPF_PROG_KVER_RANGE("schedcls/tether_downstream6_rawip$4_14", AID_ROOT, AID_NETWORK_STACK,
-                           sched_cls_tether_downstream6_rawip_4_14, KVER_4_14, KVER_5_4)
+DEFINE_BPF_PROG_KVER_RANGE(schedcls, tether_downstream6_rawip, 4_14, AID_NETWORK_STACK, 4_14, 5_4)
 (struct __sk_buff* skb) {
     return do_forward6(skb, RAWIP, DOWNSTREAM, KVER_4_14);
 }
 
-DEFINE_BPF_PROG_KVER_RANGE("schedcls/tether_upstream6_rawip$4_14", AID_ROOT, AID_NETWORK_STACK,
-                           sched_cls_tether_upstream6_rawip_4_14, KVER_4_14, KVER_5_4)
+DEFINE_BPF_PROG_KVER_RANGE(schedcls, tether_upstream6_rawip, 4_14, AID_NETWORK_STACK, 4_14, 5_4)
 (struct __sk_buff* skb) {
     return do_forward6(skb, RAWIP, UPSTREAM, KVER_4_14);
 }
 
 // and define no-op stubs for pre-4.14 kernels.
-DEFINE_BPF_PROG_KVER_RANGE("schedcls/tether_downstream6_rawip$stub", AID_ROOT, AID_NETWORK_STACK,
-                           sched_cls_tether_downstream6_rawip_stub, KVER_NONE, KVER_4_14)
+DEFINE_BPF_PROG_KVER_RANGE(schedcls, tether_downstream6_rawip, stub, AID_NETWORK_STACK, 4_9, 4_14)
 (__unused struct __sk_buff* skb) {
     return TC_ACT_PIPE;
 }
 
-DEFINE_BPF_PROG_KVER_RANGE("schedcls/tether_upstream6_rawip$stub", AID_ROOT, AID_NETWORK_STACK,
-                           sched_cls_tether_upstream6_rawip_stub, KVER_NONE, KVER_4_14)
+DEFINE_BPF_PROG_KVER_RANGE(schedcls, tether_upstream6_rawip, stub, AID_NETWORK_STACK, 4_9, 4_14)
 (__unused struct __sk_buff* skb) {
     return TC_ACT_PIPE;
 }
@@ -644,26 +634,22 @@
 
 // Full featured (required) implementations for 5.10+ kernels (these are S+ by definition)
 
-DEFINE_BPF_PROG_KVER("schedcls/tether_downstream4_rawip$5_10", AID_ROOT, AID_NETWORK_STACK,
-                     sched_cls_tether_downstream4_rawip_5_10, KVER_5_10)
+DEFINE_BPF_PROG_KVER(schedcls, tether_downstream4_rawip, 5_10, AID_NETWORK_STACK, 5_10)
 (struct __sk_buff* skb) {
     return do_forward4(skb, RAWIP, DOWNSTREAM, UPDATETIME, KVER_5_10);
 }
 
-DEFINE_BPF_PROG_KVER("schedcls/tether_upstream4_rawip$5_10", AID_ROOT, AID_NETWORK_STACK,
-                     sched_cls_tether_upstream4_rawip_5_10, KVER_5_10)
+DEFINE_BPF_PROG_KVER(schedcls, tether_upstream4_rawip, 5_10, AID_NETWORK_STACK, 5_10)
 (struct __sk_buff* skb) {
     return do_forward4(skb, RAWIP, UPSTREAM, UPDATETIME, KVER_5_10);
 }
 
-DEFINE_BPF_PROG_KVER("schedcls/tether_downstream4_ether$5_10", AID_ROOT, AID_NETWORK_STACK,
-                     sched_cls_tether_downstream4_ether_5_10, KVER_5_10)
+DEFINE_BPF_PROG_KVER(schedcls, tether_downstream4_ether, 5_10, AID_NETWORK_STACK, 5_10)
 (struct __sk_buff* skb) {
     return do_forward4(skb, ETHER, DOWNSTREAM, UPDATETIME, KVER_5_10);
 }
 
-DEFINE_BPF_PROG_KVER("schedcls/tether_upstream4_ether$5_10", AID_ROOT, AID_NETWORK_STACK,
-                     sched_cls_tether_upstream4_ether_5_10, KVER_5_10)
+DEFINE_BPF_PROG_KVER(schedcls, tether_upstream4_ether, 5_10, AID_NETWORK_STACK, 5_10)
 (struct __sk_buff* skb) {
     return do_forward4(skb, ETHER, UPSTREAM, UPDATETIME, KVER_5_10);
 }
@@ -671,34 +657,26 @@
 // Full featured (optional) implementations for 5.4-S kernels
 // (optional, because we need to be able to fallback for 5.4 pre-S kernels)
 
-DEFINE_OPTIONAL_BPF_PROG_KVER_RANGE("schedcls/tether_downstream4_rawip$opt54",
-                                    AID_ROOT, AID_NETWORK_STACK,
-                                    sched_cls_tether_downstream4_rawip_opt54,
-                                    KVER_5_4, KVER_5_10)
+DEFINE_OPTIONAL_BPF_PROG_KVER_RANGE(schedcls, tether_downstream4_rawip, opt54,
+                                    AID_NETWORK_STACK, 5_4, 5_10)
 (struct __sk_buff* skb) {
     return do_forward4(skb, RAWIP, DOWNSTREAM, UPDATETIME, KVER_5_4);
 }
 
-DEFINE_OPTIONAL_BPF_PROG_KVER_RANGE("schedcls/tether_upstream4_rawip$opt54",
-                                    AID_ROOT, AID_NETWORK_STACK,
-                                    sched_cls_tether_upstream4_rawip_opt54,
-                                    KVER_5_4, KVER_5_10)
+DEFINE_OPTIONAL_BPF_PROG_KVER_RANGE(schedcls, tether_upstream4_rawip, opt54,
+                                    AID_NETWORK_STACK, 5_4, 5_10)
 (struct __sk_buff* skb) {
     return do_forward4(skb, RAWIP, UPSTREAM, UPDATETIME, KVER_5_4);
 }
 
-DEFINE_OPTIONAL_BPF_PROG_KVER_RANGE("schedcls/tether_downstream4_ether$opt54",
-                                    AID_ROOT, AID_NETWORK_STACK,
-                                    sched_cls_tether_downstream4_ether_opt54,
-                                    KVER_5_4, KVER_5_10)
+DEFINE_OPTIONAL_BPF_PROG_KVER_RANGE(schedcls, tether_downstream4_ether, opt54,
+                                    AID_NETWORK_STACK, 5_4, 5_10)
 (struct __sk_buff* skb) {
     return do_forward4(skb, ETHER, DOWNSTREAM, UPDATETIME, KVER_5_4);
 }
 
-DEFINE_OPTIONAL_BPF_PROG_KVER_RANGE("schedcls/tether_upstream4_ether$opt54",
-                                    AID_ROOT, AID_NETWORK_STACK,
-                                    sched_cls_tether_upstream4_ether_opt54,
-                                    KVER_5_4, KVER_5_10)
+DEFINE_OPTIONAL_BPF_PROG_KVER_RANGE(schedcls, tether_upstream4_ether, opt54,
+                                    AID_NETWORK_STACK, 5_4, 5_10)
 (struct __sk_buff* skb) {
     return do_forward4(skb, ETHER, UPSTREAM, UPDATETIME, KVER_5_4);
 }
@@ -706,34 +684,26 @@
 // Full featured (optional) implementations for 4.14-S & 4.19-S kernels
 // (optional, because we need to be able to fallback for 4.14/4.19 pre-S kernels)
 
-DEFINE_OPTIONAL_BPF_PROG_KVER_RANGE("schedcls/tether_downstream4_rawip$opt",
-                                    AID_ROOT, AID_NETWORK_STACK,
-                                    sched_cls_tether_downstream4_rawip_opt,
-                                    KVER_4_14, KVER_5_4)
+DEFINE_OPTIONAL_BPF_PROG_KVER_RANGE(schedcls, tether_downstream4_rawip, opt,
+                                    AID_NETWORK_STACK, 4_14, 5_4)
 (struct __sk_buff* skb) {
     return do_forward4(skb, RAWIP, DOWNSTREAM, UPDATETIME, KVER_4_14);
 }
 
-DEFINE_OPTIONAL_BPF_PROG_KVER_RANGE("schedcls/tether_upstream4_rawip$opt",
-                                    AID_ROOT, AID_NETWORK_STACK,
-                                    sched_cls_tether_upstream4_rawip_opt,
-                                    KVER_4_14, KVER_5_4)
+DEFINE_OPTIONAL_BPF_PROG_KVER_RANGE(schedcls, tether_upstream4_rawip, opt,
+                                    AID_NETWORK_STACK, 4_14, 5_4)
 (struct __sk_buff* skb) {
     return do_forward4(skb, RAWIP, UPSTREAM, UPDATETIME, KVER_4_14);
 }
 
-DEFINE_OPTIONAL_BPF_PROG_KVER_RANGE("schedcls/tether_downstream4_ether$opt",
-                                    AID_ROOT, AID_NETWORK_STACK,
-                                    sched_cls_tether_downstream4_ether_opt,
-                                    KVER_4_14, KVER_5_4)
+DEFINE_OPTIONAL_BPF_PROG_KVER_RANGE(schedcls, tether_downstream4_ether, opt,
+                                    AID_NETWORK_STACK, 4_14, 5_4)
 (struct __sk_buff* skb) {
     return do_forward4(skb, ETHER, DOWNSTREAM, UPDATETIME, KVER_4_14);
 }
 
-DEFINE_OPTIONAL_BPF_PROG_KVER_RANGE("schedcls/tether_upstream4_ether$opt",
-                                    AID_ROOT, AID_NETWORK_STACK,
-                                    sched_cls_tether_upstream4_ether_opt,
-                                    KVER_4_14, KVER_5_4)
+DEFINE_OPTIONAL_BPF_PROG_KVER_RANGE(schedcls, tether_upstream4_ether, opt,
+                                    AID_NETWORK_STACK, 4_14, 5_4)
 (struct __sk_buff* skb) {
     return do_forward4(skb, ETHER, UPSTREAM, UPDATETIME, KVER_4_14);
 }
@@ -752,14 +722,12 @@
 
 // RAWIP: Required for 5.4-R kernels -- which always support bpf_skb_change_head().
 
-DEFINE_BPF_PROG_KVER_RANGE("schedcls/tether_downstream4_rawip$5_4", AID_ROOT, AID_NETWORK_STACK,
-                           sched_cls_tether_downstream4_rawip_5_4, KVER_5_4, KVER_5_10)
+DEFINE_BPF_PROG_KVER_RANGE(schedcls, tether_downstream4_rawip, 5_4, AID_NETWORK_STACK, 5_4, 5_10)
 (struct __sk_buff* skb) {
     return do_forward4(skb, RAWIP, DOWNSTREAM, NO_UPDATETIME, KVER_5_4);
 }
 
-DEFINE_BPF_PROG_KVER_RANGE("schedcls/tether_upstream4_rawip$5_4", AID_ROOT, AID_NETWORK_STACK,
-                           sched_cls_tether_upstream4_rawip_5_4, KVER_5_4, KVER_5_10)
+DEFINE_BPF_PROG_KVER_RANGE(schedcls, tether_upstream4_rawip, 5_4, AID_NETWORK_STACK, 5_4, 5_10)
 (struct __sk_buff* skb) {
     return do_forward4(skb, RAWIP, UPSTREAM, NO_UPDATETIME, KVER_5_4);
 }
@@ -767,32 +735,26 @@
 // RAWIP: Optional for 4.14/4.19 (R) kernels -- which support bpf_skb_change_head().
 // [Note: fallback for 4.14/4.19 (P/Q) kernels is below in stub section]
 
-DEFINE_OPTIONAL_BPF_PROG_KVER_RANGE("schedcls/tether_downstream4_rawip$4_14",
-                                    AID_ROOT, AID_NETWORK_STACK,
-                                    sched_cls_tether_downstream4_rawip_4_14,
-                                    KVER_4_14, KVER_5_4)
+DEFINE_OPTIONAL_BPF_PROG_KVER_RANGE(schedcls, tether_downstream4_rawip, 4_14,
+                                    AID_NETWORK_STACK, 4_14, 5_4)
 (struct __sk_buff* skb) {
     return do_forward4(skb, RAWIP, DOWNSTREAM, NO_UPDATETIME, KVER_4_14);
 }
 
-DEFINE_OPTIONAL_BPF_PROG_KVER_RANGE("schedcls/tether_upstream4_rawip$4_14",
-                                    AID_ROOT, AID_NETWORK_STACK,
-                                    sched_cls_tether_upstream4_rawip_4_14,
-                                    KVER_4_14, KVER_5_4)
+DEFINE_OPTIONAL_BPF_PROG_KVER_RANGE(schedcls, tether_upstream4_rawip, 4_14,
+                                    AID_NETWORK_STACK, 4_14, 5_4)
 (struct __sk_buff* skb) {
     return do_forward4(skb, RAWIP, UPSTREAM, NO_UPDATETIME, KVER_4_14);
 }
 
 // ETHER: Required for 4.14-Q/R, 4.19-Q/R & 5.4-R kernels.
 
-DEFINE_BPF_PROG_KVER_RANGE("schedcls/tether_downstream4_ether$4_14", AID_ROOT, AID_NETWORK_STACK,
-                           sched_cls_tether_downstream4_ether_4_14, KVER_4_14, KVER_5_10)
+DEFINE_BPF_PROG_KVER_RANGE(schedcls, tether_downstream4_ether, 4_14, AID_NETWORK_STACK, 4_14, 5_10)
 (struct __sk_buff* skb) {
     return do_forward4(skb, ETHER, DOWNSTREAM, NO_UPDATETIME, KVER_4_14);
 }
 
-DEFINE_BPF_PROG_KVER_RANGE("schedcls/tether_upstream4_ether$4_14", AID_ROOT, AID_NETWORK_STACK,
-                           sched_cls_tether_upstream4_ether_4_14, KVER_4_14, KVER_5_10)
+DEFINE_BPF_PROG_KVER_RANGE(schedcls, tether_upstream4_ether, 4_14, AID_NETWORK_STACK, 4_14, 5_10)
 (struct __sk_buff* skb) {
     return do_forward4(skb, ETHER, UPSTREAM, NO_UPDATETIME, KVER_4_14);
 }
@@ -801,28 +763,24 @@
 
 // RAWIP: 4.9-P/Q, 4.14-P/Q & 4.19-Q kernels -- without bpf_skb_change_head() for tc programs
 
-DEFINE_BPF_PROG_KVER_RANGE("schedcls/tether_downstream4_rawip$stub", AID_ROOT, AID_NETWORK_STACK,
-                           sched_cls_tether_downstream4_rawip_stub, KVER_NONE, KVER_5_4)
+DEFINE_BPF_PROG_KVER_RANGE(schedcls, tether_downstream4_rawip, stub, AID_NETWORK_STACK, 4_9, 5_4)
 (__unused struct __sk_buff* skb) {
     return TC_ACT_PIPE;
 }
 
-DEFINE_BPF_PROG_KVER_RANGE("schedcls/tether_upstream4_rawip$stub", AID_ROOT, AID_NETWORK_STACK,
-                           sched_cls_tether_upstream4_rawip_stub, KVER_NONE, KVER_5_4)
+DEFINE_BPF_PROG_KVER_RANGE(schedcls, tether_upstream4_rawip, stub, AID_NETWORK_STACK, 4_9, 5_4)
 (__unused struct __sk_buff* skb) {
     return TC_ACT_PIPE;
 }
 
 // ETHER: 4.9-P/Q kernel
 
-DEFINE_BPF_PROG_KVER_RANGE("schedcls/tether_downstream4_ether$stub", AID_ROOT, AID_NETWORK_STACK,
-                           sched_cls_tether_downstream4_ether_stub, KVER_NONE, KVER_4_14)
+DEFINE_BPF_PROG_KVER_RANGE(schedcls, tether_downstream4_ether, stub, AID_NETWORK_STACK, 4_9, 4_14)
 (__unused struct __sk_buff* skb) {
     return TC_ACT_PIPE;
 }
 
-DEFINE_BPF_PROG_KVER_RANGE("schedcls/tether_upstream4_ether$stub", AID_ROOT, AID_NETWORK_STACK,
-                           sched_cls_tether_upstream4_ether_stub, KVER_NONE, KVER_4_14)
+DEFINE_BPF_PROG_KVER_RANGE(schedcls, tether_upstream4_ether, stub, AID_NETWORK_STACK, 4_9, 4_14)
 (__unused struct __sk_buff* skb) {
     return TC_ACT_PIPE;
 }
diff --git a/bpf/progs/offload@mainline.c b/bpf/progs/offload@mainline.c
deleted file mode 120000
index 4092e0d..0000000
--- a/bpf/progs/offload@mainline.c
+++ /dev/null
@@ -1 +0,0 @@
-offload.c
\ No newline at end of file
diff --git a/bpf/progs/test@mainline.c b/bpf/progs/test@mainline.c
deleted file mode 120000
index aeebb26..0000000
--- a/bpf/progs/test@mainline.c
+++ /dev/null
@@ -1 +0,0 @@
-test.c
\ No newline at end of file
diff --git a/clatd/main.c b/clatd/main.c
index 8fc9eda..de1c36a 100644
--- a/clatd/main.c
+++ b/clatd/main.c
@@ -37,27 +37,11 @@
 #include "config.h"
 #include "logging.h"
 
-#define DEVICEPREFIX "v4-"
-
 /* function: handle_sigterm
  * signal handler: stop the event loop
  */
 static void handle_sigterm(__attribute__((unused)) int unused) { sigterm = 1; };
 
-/* function: print_help
- * in case the user is running this on the command line
- */
-void print_help() {
-  printf("android-clat arguments:\n");
-  printf("-i [uplink interface]\n");
-  printf("-p [plat prefix]\n");
-  printf("-4 [IPv4 address]\n");
-  printf("-6 [IPv6 address]\n");
-  printf("-t [tun file descriptor number]\n");
-  printf("-r [read socket descriptor number]\n");
-  printf("-w [write socket descriptor number]\n");
-}
-
 // Load the architecture identifier (AUDIT_ARCH_* constant)
 #define BPF_SECCOMP_LOAD_AUDIT_ARCH \
 	BPF_STMT(BPF_LD | BPF_W | BPF_ABS, offsetof(struct seccomp_data, arch))
@@ -139,6 +123,9 @@
     BPF2_SECCOMP_ALLOW_IF_EQUAL(__NR_sendmsg),       // 211
     BPF2_SECCOMP_ALLOW_IF_EQUAL(__NR_recvmsg),       // 212
     BPF2_SECCOMP_ALLOW_IF_EQUAL(__NR_writev),        // 66
+#if defined(__i386__)
+    BPF2_SECCOMP_ALLOW_IF_EQUAL(__NR_socketcall),    // x86-32 only
+#endif
 
     // logging: getuid writev
     BPF2_SECCOMP_ALLOW_IF_EQUAL(__NR_getuid),        // 174
@@ -187,13 +174,6 @@
  * allocate and setup the tun device, then run the event loop
  */
 int main(int argc, char **argv) {
-  struct tun_data tunnel;
-  int opt;
-  char *uplink_interface = NULL, *plat_prefix = NULL;
-  char *v4_addr = NULL, *v6_addr = NULL, *tunfd_str = NULL, *read_sock_str = NULL,
-       *write_sock_str = NULL;
-  unsigned len;
-
   // Clatd binary is setuid/gid CLAT, thus when we reach here we have:
   //   $ adb shell ps | grep clat
   //                [pid] [ppid]
@@ -236,44 +216,23 @@
   // Gid:    1029    1029    1029    1029
   // Groups: 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1018 1021 1023 1024 1032 1065 3001 3002 3003 3005 3006 3007 3009 3010 3011 3012
 
-  while ((opt = getopt(argc, argv, "i:p:4:6:t:r:w:h")) != -1) {
-    switch (opt) {
-      case 'i':
-        uplink_interface = optarg;
-        break;
-      case 'p':
-        plat_prefix = optarg;
-        break;
-      case '4':
-        v4_addr = optarg;
-        break;
-      case '6':
-        v6_addr = optarg;
-        break;
-      case 't':
-        tunfd_str = optarg;
-        break;
-      case 'r':
-        read_sock_str = optarg;
-        break;
-      case 'w':
-        write_sock_str = optarg;
-        break;
-      case 'h':
-        print_help();
-        exit(0);
-      default:
-        logmsg(ANDROID_LOG_FATAL, "Unknown option -%c. Exiting.", (char)optopt);
-        exit(1);
-    }
-  }
-
-  if (uplink_interface == NULL) {
-    logmsg(ANDROID_LOG_FATAL, "clatd called without an interface");
+  if (argc != 8) {
+    logmsg(ANDROID_LOG_FATAL, "incorrect arguments, expect: "
+           "uplink_interface plat_prefix IPv4_addr IPv6_addr tun_fd read_sock_fd write_sock_fd");
     exit(1);
   }
 
-  if (tunfd_str != NULL && !parse_int(tunfd_str, &tunnel.fd4)) {
+  const char* const uplink_interface = argv[1];
+  const char* const plat_prefix = argv[2];
+  const char* const v4_addr = argv[3];
+  const char* const v6_addr = argv[4];
+  const char* const tunfd_str = argv[5];
+  const char* const read_sock_str = argv[6];
+  const char* const write_sock_str = argv[7];
+
+  struct tun_data tunnel;
+
+  if (!parse_int(tunfd_str, &tunnel.fd4)) {
     logmsg(ANDROID_LOG_FATAL, "invalid tunfd %s", tunfd_str);
     exit(1);
   }
@@ -282,7 +241,7 @@
     exit(1);
   }
 
-  if (read_sock_str != NULL && !parse_int(read_sock_str, &tunnel.read_fd6)) {
+  if (!parse_int(read_sock_str, &tunnel.read_fd6)) {
     logmsg(ANDROID_LOG_FATAL, "invalid read socket %s", read_sock_str);
     exit(1);
   }
@@ -291,7 +250,7 @@
     exit(1);
   }
 
-  if (write_sock_str != NULL && !parse_int(write_sock_str, &tunnel.write_fd6)) {
+  if (!parse_int(write_sock_str, &tunnel.write_fd6)) {
     logmsg(ANDROID_LOG_FATAL, "invalid write socket %s", write_sock_str);
     exit(1);
   }
@@ -300,24 +259,24 @@
     exit(1);
   }
 
-  len = snprintf(tunnel.device4, sizeof(tunnel.device4), "%s%s", DEVICEPREFIX, uplink_interface);
+  unsigned len = snprintf(tunnel.device4, sizeof(tunnel.device4), "v4-%s", uplink_interface);
   if (len >= sizeof(tunnel.device4)) {
     logmsg(ANDROID_LOG_FATAL, "interface name too long '%s'", tunnel.device4);
     exit(1);
   }
 
   Global_Clatd_Config.native_ipv6_interface = uplink_interface;
-  if (!plat_prefix || inet_pton(AF_INET6, plat_prefix, &Global_Clatd_Config.plat_subnet) <= 0) {
+  if (inet_pton(AF_INET6, plat_prefix, &Global_Clatd_Config.plat_subnet) <= 0) {
     logmsg(ANDROID_LOG_FATAL, "invalid IPv6 address specified for plat prefix: %s", plat_prefix);
     exit(1);
   }
 
-  if (!v4_addr || !inet_pton(AF_INET, v4_addr, &Global_Clatd_Config.ipv4_local_subnet.s_addr)) {
+  if (!inet_pton(AF_INET, v4_addr, &Global_Clatd_Config.ipv4_local_subnet.s_addr)) {
     logmsg(ANDROID_LOG_FATAL, "Invalid IPv4 address %s", v4_addr);
     exit(1);
   }
 
-  if (!v6_addr || !inet_pton(AF_INET6, v6_addr, &Global_Clatd_Config.ipv6_local_subnet)) {
+  if (!inet_pton(AF_INET6, v6_addr, &Global_Clatd_Config.ipv6_local_subnet)) {
     logmsg(ANDROID_LOG_FATAL, "Invalid source address %s", v6_addr);
     exit(1);
   }
diff --git a/framework/src/android/net/connectivity/ConnectivityInternalApiUtil.java b/framework/src/android/net/connectivity/ConnectivityInternalApiUtil.java
index 9478e91..dfb7844 100644
--- a/framework/src/android/net/connectivity/ConnectivityInternalApiUtil.java
+++ b/framework/src/android/net/connectivity/ConnectivityInternalApiUtil.java
@@ -20,6 +20,7 @@
 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED;
 
 import android.annotation.SuppressLint;
 import android.content.Context;
@@ -86,12 +87,15 @@
     public static NetworkAgent buildTetheringNetworkAgent(@NonNull Context ctx,
             @NonNull Looper looper, @NonNull String logTag, int transportType,
             @NonNull LinkProperties lp) {
+        // LINT.IfChange
         final NetworkCapabilities.Builder builder = new NetworkCapabilities.Builder()
                 .addCapability(NET_CAPABILITY_NOT_METERED)
                 .addCapability(NET_CAPABILITY_NOT_ROAMING)
                 .addCapability(NET_CAPABILITY_NOT_CONGESTED)
                 .addCapability(NET_CAPABILITY_NOT_SUSPENDED)
+                .addCapability(NET_CAPABILITY_NOT_VCN_MANAGED)
                 .addTransportType(transportType);
+        // LINT.ThenChange(../../../../../service/src/com/android/metrics/SatisfiedByLocalNetworkMetrics.java)
         // TODO: Change to use the constant definition. Flags.netCapabilityLocalNetwork() was not
         //  fully rolled out but the service will still process this capability, set it anyway.
         builder.addCapability(36 /* NET_CAPABILITY_LOCAL_NETWORK */);
diff --git a/service/jni/com_android_server_connectivity_ClatCoordinator.cpp b/service/jni/com_android_server_connectivity_ClatCoordinator.cpp
index 622fba8..70991eb 100644
--- a/service/jni/com_android_server_connectivity_ClatCoordinator.cpp
+++ b/service/jni/com_android_server_connectivity_ClatCoordinator.cpp
@@ -397,13 +397,13 @@
 
     // clang-format off
     const char* args[] = {progname.c_str(),
-                          "-i", ifaceStr.c_str(),
-                          "-p", pfx96Str.c_str(),
-                          "-4", v4Str.c_str(),
-                          "-6", v6Str.c_str(),
-                          "-t", tunFdStr,
-                          "-r", sockReadStr,
-                          "-w", sockWriteStr,
+                          ifaceStr.c_str(),
+                          pfx96Str.c_str(),
+                          v4Str.c_str(),
+                          v6Str.c_str(),
+                          tunFdStr,
+                          sockReadStr,
+                          sockWriteStr,
                           nullptr};
     // clang-format on
 
diff --git a/service/src/com/android/metrics/SatisfiedByLocalNetworkMetrics.java b/service/src/com/android/metrics/SatisfiedByLocalNetworkMetrics.java
index b256a19..d45116e 100644
--- a/service/src/com/android/metrics/SatisfiedByLocalNetworkMetrics.java
+++ b/service/src/com/android/metrics/SatisfiedByLocalNetworkMetrics.java
@@ -21,6 +21,7 @@
 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED;
 
 import static com.android.net.module.util.FrameworkConnectivityStatsLog.SATISFIED_BY_LOCAL_NETWORK_REQUESTS;
 import static com.android.net.module.util.FrameworkConnectivityStatsLog.SATISFIED_BY_LOCAL_NETWORK_REQUESTS__TYPE__NETWORK_REQUEST_TYPE_BACKGROUND_REQUEST;
@@ -107,6 +108,7 @@
                     .addCapability(NET_CAPABILITY_NOT_ROAMING)
                     .addCapability(NET_CAPABILITY_NOT_CONGESTED)
                     .addCapability(NET_CAPABILITY_NOT_SUSPENDED)
+                    .addCapability(NET_CAPABILITY_NOT_VCN_MANAGED)
                     .addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
                     .addTransportType(NetworkCapabilities.TRANSPORT_USB)
                     .addTransportType(NetworkCapabilities.TRANSPORT_ETHERNET)
@@ -218,4 +220,3 @@
         pw.println("SatisfiedByLocalNetworkMetrics: " + mSatisfiedRequestsPerTypeAndUid);
     }
 }
-
diff --git a/service/src/com/android/server/ConnectivityService.java b/service/src/com/android/server/ConnectivityService.java
index ed74289..57f2ad4 100644
--- a/service/src/com/android/server/ConnectivityService.java
+++ b/service/src/com/android/server/ConnectivityService.java
@@ -112,6 +112,7 @@
 import static android.net.NetworkCapabilities.RES_ID_MATCH_ALL_RESERVATIONS;
 import static android.net.NetworkCapabilities.RES_ID_UNSET;
 import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
+import static android.net.NetworkCapabilities.TRANSPORT_SATELLITE;
 import static android.net.NetworkCapabilities.TRANSPORT_TEST;
 import static android.net.NetworkCapabilities.TRANSPORT_THREAD;
 import static android.net.NetworkCapabilities.TRANSPORT_VPN;
@@ -169,6 +170,7 @@
 import static com.android.server.connectivity.ConnectivityFlags.QUEUE_NETWORK_AGENT_EVENTS_IN_SYSTEM_SERVER;
 import static com.android.server.connectivity.ConnectivityFlags.REQUEST_RESTRICTED_WIFI;
 import static com.android.server.connectivity.ConnectivityFlags.SATISFIED_BY_LOCAL_NETWORK_METRICS;
+import static com.android.server.connectivity.ConnectivityFlags.USE_SATELLITE_REPORTED_SUSPENDED_AND_ROAMING;
 import static com.android.server.connectivity.ConnectivityFlags.WIFI_DATA_INACTIVITY_TIMEOUT;
 
 import android.Manifest;
@@ -360,6 +362,7 @@
 import com.android.net.module.util.LinkPropertiesUtils.CompareOrUpdateResult;
 import com.android.net.module.util.LinkPropertiesUtils.CompareResult;
 import com.android.net.module.util.LocationPermissionChecker;
+import com.android.net.module.util.NetdUtils;
 import com.android.net.module.util.PerUidCounter;
 import com.android.net.module.util.PermissionUtils;
 import com.android.net.module.util.RoutingCoordinatorService;
@@ -572,6 +575,7 @@
     // Flag for early link properties update
     private final boolean mSupportEarlyLinkPropertiesUpdateForVPN;
     private final boolean mConstrainedDataSatelliteMetrics;
+    private final boolean mUseSatelliteReportedSuspendedAndRoaming;
 
     /**
      * Uids ConnectivityService tracks blocked status of to send blocked status callbacks.
@@ -1588,6 +1592,15 @@
         }
 
         /**
+         * Register a content observer. This method exists because ContentResolver#registerObserver
+         * is final and cannot be overridden by tests.
+         */
+        public void registerContentObserver(ContentResolver cr, Uri uri,
+                boolean notifyForDescendants, ContentObserver observer) {
+            cr.registerContentObserver(uri, notifyForDescendants, observer);
+        }
+
+        /**
          * Get a reference to the ModuleNetworkStackClient.
          */
         public NetworkStackClientBase getNetworkStack() {
@@ -2249,7 +2262,7 @@
             loge("Error registering event listener :" + e);
         }
 
-        mSettingsObserver = new SettingsObserver(mContext, mHandler);
+        mSettingsObserver = new SettingsObserver(mContext, mHandler, mDeps);
         registerSettingsCallbacks();
 
         mKeepaliveTracker = mDeps.makeAutomaticOnOffKeepaliveTracker(mContext, mHandler);
@@ -2358,6 +2371,9 @@
         } else {
             mQuicConnectionCloser = null;
         }
+
+        mUseSatelliteReportedSuspendedAndRoaming = mDeps.isFeatureNotChickenedOut(
+                context, USE_SATELLITE_REPORTED_SUSPENDED_AND_ROAMING);
     }
 
     /**
@@ -2426,33 +2442,6 @@
         return new NetworkRequest(netCap, TYPE_NONE, nextNetworkRequestId(), type);
     }
 
-    // Used only for testing.
-    // TODO: Delete this and either:
-    // 1. Give FakeSettingsProvider the ability to send settings change notifications (requires
-    //    changing ContentResolver to make registerContentObserver non-final).
-    // 2. Give FakeSettingsProvider an alternative notification mechanism and have the test use it
-    //    by subclassing SettingsObserver.
-    @VisibleForTesting
-    void updateAlwaysOnNetworks() {
-        mHandler.sendEmptyMessage(EVENT_CONFIGURE_ALWAYS_ON_NETWORKS);
-    }
-
-    // See FakeSettingsProvider comment above.
-    @VisibleForTesting
-    void updatePrivateDnsSettings() {
-        mHandler.sendEmptyMessage(EVENT_PRIVATE_DNS_SETTINGS_CHANGED);
-    }
-
-    @VisibleForTesting
-    public void updateMobileDataPreferredUids() {
-        mHandler.sendEmptyMessage(EVENT_MOBILE_DATA_PREFERRED_UIDS_CHANGED);
-    }
-
-    @VisibleForTesting
-    void updateIngressRateLimit() {
-        mHandler.sendEmptyMessage(EVENT_INGRESS_RATE_LIMIT_CHANGED);
-    }
-
     @VisibleForTesting
     void simulateUpdateProxyInfo(@Nullable final Network network,
             @NonNull final ProxyInfo proxyInfo) {
@@ -4402,7 +4391,7 @@
         // rules are applied before system ready. Normally, the empty uid list means to clear
         // the uids rules on netd.
         if (!ConnectivitySettingsManager.getMobileDataPreferredUids(mContext).isEmpty()) {
-            updateMobileDataPreferredUids();
+            mHandler.sendEmptyMessage(EVENT_MOBILE_DATA_PREFERRED_UIDS_CHANGED);
         }
 
         if (mSatelliteAccessController != null) {
@@ -7685,21 +7674,22 @@
     }
 
     private static class SettingsObserver extends ContentObserver {
-        final private HashMap<Uri, Integer> mUriEventMap;
-        final private Context mContext;
-        final private Handler mHandler;
+        private final HashMap<Uri, Integer> mUriEventMap;
+        private final Context mContext;
+        private final Handler mHandler;
+        private final Dependencies mDeps;
 
-        SettingsObserver(Context context, Handler handler) {
+        SettingsObserver(Context context, Handler handler, Dependencies deps) {
             super(null);
             mUriEventMap = new HashMap<>();
             mContext = context;
             mHandler = handler;
+            mDeps = deps;
         }
 
         void observe(Uri uri, int what) {
             mUriEventMap.put(uri, what);
-            final ContentResolver resolver = mContext.getContentResolver();
-            resolver.registerContentObserver(uri, false, this);
+            mDeps.registerContentObserver(mContext.getContentResolver(), uri, false, this);
         }
 
         @Override
@@ -10594,7 +10584,8 @@
             if (route.hasGateway()) continue;
             if (VDBG || DDBG) log("Adding Route [" + route + "] to network " + netId);
             try {
-                mRoutingCoordinatorService.addRoute(netId, route);
+                mRoutingCoordinatorService.addRouteParcel(netId,
+                        NetdUtils.toRouteInfoParcel(route));
             } catch (Exception e) {
                 if ((route.getDestination().getAddress() instanceof Inet4Address) || VDBG) {
                     loge("Exception in addRoute for non-gateway: " + e);
@@ -10605,7 +10596,8 @@
             if (!route.hasGateway()) continue;
             if (VDBG || DDBG) log("Adding Route [" + route + "] to network " + netId);
             try {
-                mRoutingCoordinatorService.addRoute(netId, route);
+                mRoutingCoordinatorService.addRouteParcel(netId,
+                        NetdUtils.toRouteInfoParcel(route));
             } catch (Exception e) {
                 if ((route.getGateway() instanceof Inet4Address) || VDBG) {
                     loge("Exception in addRoute for gateway: " + e);
@@ -10616,7 +10608,8 @@
         for (RouteInfo route : routeDiff.removed) {
             if (VDBG || DDBG) log("Removing Route [" + route + "] from network " + netId);
             try {
-                mRoutingCoordinatorService.removeRoute(netId, route);
+                mRoutingCoordinatorService.removeRouteParcel(netId,
+                        NetdUtils.toRouteInfoParcel(route));
             } catch (Exception e) {
                 loge("Exception in removeRoute: " + e);
             }
@@ -10625,7 +10618,8 @@
         for (RouteInfo route : routeDiff.updated) {
             if (VDBG || DDBG) log("Updating Route [" + route + "] from network " + netId);
             try {
-                mRoutingCoordinatorService.updateRoute(netId, route);
+                mRoutingCoordinatorService.updateRouteParcel(netId,
+                        NetdUtils.toRouteInfoParcel(route));
             } catch (Exception e) {
                 loge("Exception in updateRoute: " + e);
             }
@@ -10926,7 +10920,9 @@
         newNc.setPrivateDnsBroken(nai.networkCapabilities.isPrivateDnsBroken());
 
         // TODO : remove this once all factories are updated to send NOT_SUSPENDED and NOT_ROAMING
-        if (!newNc.hasTransport(TRANSPORT_CELLULAR)) {
+        if (!newNc.hasTransport(TRANSPORT_CELLULAR)
+                && !(mUseSatelliteReportedSuspendedAndRoaming
+                     && newNc.hasTransport(TRANSPORT_SATELLITE))) {
             newNc.addCapability(NET_CAPABILITY_NOT_SUSPENDED);
             newNc.addCapability(NET_CAPABILITY_NOT_ROAMING);
         }
diff --git a/service/src/com/android/server/connectivity/ConnectivityFlags.java b/service/src/com/android/server/connectivity/ConnectivityFlags.java
index 19ea0cc..20b3ce9 100644
--- a/service/src/com/android/server/connectivity/ConnectivityFlags.java
+++ b/service/src/com/android/server/connectivity/ConnectivityFlags.java
@@ -76,6 +76,9 @@
     public static final String SATISFIED_BY_LOCAL_NETWORK_METRICS =
             "satisfied_by_local_network_metrics";
 
+    public static final String USE_SATELLITE_REPORTED_SUSPENDED_AND_ROAMING =
+            "use_satellite_reported_suspended_and_roaming";
+
     /**
      * A feature flag to control whether the early link properties update for vpn should be enabled.
      *
diff --git a/service/src/com/android/server/connectivity/MultinetworkPolicyTracker.java b/service/src/com/android/server/connectivity/MultinetworkPolicyTracker.java
index a614cc4..cde16d8 100644
--- a/service/src/com/android/server/connectivity/MultinetworkPolicyTracker.java
+++ b/service/src/com/android/server/connectivity/MultinetworkPolicyTracker.java
@@ -321,7 +321,11 @@
     @TargetApi(Build.VERSION_CODES.S)
     public void start() {
         for (Uri uri : mSettingsUris) {
-            mResolver.registerContentObserver(uri, true, mSettingObserver);
+            mResolver.registerContentObserver(
+                    uri,
+                    mAvoidBadWifiSource == FROM_CARRIER_CONFIG,
+                    mSettingObserver
+            );
         }
 
         final IntentFilter intentFilter = new IntentFilter();
diff --git a/staticlibs/Android.bp b/staticlibs/Android.bp
index c657a74..8849eb3 100644
--- a/staticlibs/Android.bp
+++ b/staticlibs/Android.bp
@@ -618,6 +618,9 @@
     visibility: [
         "//system/tools/aidl/build",
     ],
+    imports: [
+        "netd_aidl_interface-V17", // For RouteInfoParcel.
+    ],
 }
 
 // Use a filegroup and not a library for telephony sources, as framework-annotations cannot be
diff --git a/staticlibs/device/com/android/net/module/util/IRoutingCoordinator.aidl b/staticlibs/device/com/android/net/module/util/IRoutingCoordinator.aidl
index 7688e6a..4baeaf9 100644
--- a/staticlibs/device/com/android/net/module/util/IRoutingCoordinator.aidl
+++ b/staticlibs/device/com/android/net/module/util/IRoutingCoordinator.aidl
@@ -21,6 +21,7 @@
 import android.net.Network;
 import android.net.NetworkCapabilities;
 import android.net.RouteInfo;
+import android.net.RouteInfoParcel;
 
 import com.android.net.module.util.IIpv4PrefixRequest;
 
@@ -30,6 +31,7 @@
 // invocation to an incorrect interface" when calling the IPC.
 @Descriptor("value=no.jarjar.com.android.net.module.util.IRoutingCoordinator")
 interface IRoutingCoordinator {
+
    /**
     * Add a route for specific network
     *
@@ -38,7 +40,7 @@
     * @throws ServiceSpecificException in case of failure, with an error code indicating the
     *         cause of the failure.
     */
-    void addRoute(int netId, in RouteInfo route);
+    void addRouteParcel(int netId, in RouteInfoParcel route);
 
    /**
     * Remove a route for specific network
@@ -48,7 +50,7 @@
     * @throws ServiceSpecificException in case of failure, with an error code indicating the
     *         cause of the failure.
     */
-    void removeRoute(int netId, in RouteInfo route);
+    void removeRouteParcel(int netId, in RouteInfoParcel route);
 
     /**
     * Update a route for specific network
@@ -58,7 +60,7 @@
     * @throws ServiceSpecificException in case of failure, with an error code indicating the
     *         cause of the failure.
     */
-    void updateRoute(int netId, in RouteInfo route);
+    void updateRouteParcel(int netId, in RouteInfoParcel route);
 
     /**
      * Adds an interface to a network. The interface must not be assigned to any network, including
diff --git a/staticlibs/device/com/android/net/module/util/RoutingCoordinatorManager.java b/staticlibs/device/com/android/net/module/util/RoutingCoordinatorManager.java
index f5af30c..5234bc1 100644
--- a/staticlibs/device/com/android/net/module/util/RoutingCoordinatorManager.java
+++ b/staticlibs/device/com/android/net/module/util/RoutingCoordinatorManager.java
@@ -21,7 +21,6 @@
 import android.net.LinkProperties;
 import android.net.Network;
 import android.net.NetworkCapabilities;
-import android.net.RouteInfo;
 import android.os.IBinder;
 import android.os.RemoteException;
 
@@ -51,54 +50,6 @@
     }
 
     /**
-     * Add a route for specific network
-     *
-     * @param netId the network to add the route to
-     * @param route the route to add
-     * @throws ServiceSpecificException in case of failure, with an error code indicating the
-     *         cause of the failure.
-     */
-    public void addRoute(final int netId, final RouteInfo route) {
-        try {
-            mService.addRoute(netId, route);
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
-    }
-
-    /**
-     * Remove a route for specific network
-     *
-     * @param netId the network to remove the route from
-     * @param route the route to remove
-     * @throws ServiceSpecificException in case of failure, with an error code indicating the
-     *         cause of the failure.
-     */
-    public void removeRoute(final int netId, final RouteInfo route) {
-        try {
-            mService.removeRoute(netId, route);
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
-    }
-
-    /**
-     * Update a route for specific network
-     *
-     * @param netId the network to update the route for
-     * @param route parcelable with route information
-     * @throws ServiceSpecificException in case of failure, with an error code indicating the
-     *         cause of the failure.
-     */
-    public void updateRoute(final int netId, final RouteInfo route) {
-        try {
-            mService.updateRoute(netId, route);
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
-    }
-
-    /**
      * Adds an interface to a network. The interface must not be assigned to any network, including
      * the specified network.
      *
diff --git a/staticlibs/device/com/android/net/module/util/RoutingCoordinatorService.java b/staticlibs/device/com/android/net/module/util/RoutingCoordinatorService.java
index 61e830f..1950535 100644
--- a/staticlibs/device/com/android/net/module/util/RoutingCoordinatorService.java
+++ b/staticlibs/device/com/android/net/module/util/RoutingCoordinatorService.java
@@ -16,17 +16,14 @@
 
 package com.android.net.module.util;
 
-import static com.android.net.module.util.NetdUtils.toRouteInfoParcel;
-
 import android.annotation.NonNull;
 import android.content.Context;
 import android.net.INetd;
-
 import android.net.LinkAddress;
 import android.net.LinkProperties;
 import android.net.Network;
 import android.net.NetworkCapabilities;
-import android.net.RouteInfo;
+import android.net.RouteInfoParcel;
 import android.os.RemoteException;
 import android.os.ServiceSpecificException;
 import android.util.ArraySet;
@@ -84,9 +81,9 @@
      *         cause of the failure.
      */
     @Override
-    public void addRoute(final int netId, final RouteInfo route)
+    public void addRouteParcel(int netId, RouteInfoParcel route)
             throws ServiceSpecificException, RemoteException {
-        mNetd.networkAddRouteParcel(netId, toRouteInfoParcel(route));
+        mNetd.networkAddRouteParcel(netId, route);
     }
 
     /**
@@ -98,9 +95,9 @@
      *         cause of the failure.
      */
     @Override
-    public void removeRoute(final int netId, final RouteInfo route)
+    public void removeRouteParcel(int netId, RouteInfoParcel route)
             throws ServiceSpecificException, RemoteException {
-        mNetd.networkRemoveRouteParcel(netId, toRouteInfoParcel(route));
+        mNetd.networkRemoveRouteParcel(netId, route);
     }
 
     /**
@@ -112,9 +109,9 @@
      *         cause of the failure.
      */
     @Override
-    public void updateRoute(final int netId, final RouteInfo route)
+    public void updateRouteParcel(int netId, RouteInfoParcel route)
             throws ServiceSpecificException, RemoteException {
-        mNetd.networkUpdateRouteParcel(netId, toRouteInfoParcel(route));
+        mNetd.networkUpdateRouteParcel(netId, route);
     }
 
     /**
diff --git a/staticlibs/device/com/android/net/module/util/dhcp6/Dhcp6AddrRegInformPacket.java b/staticlibs/device/com/android/net/module/util/dhcp6/Dhcp6AddrRegInformPacket.java
index 9fd1f96..4d5478c 100644
--- a/staticlibs/device/com/android/net/module/util/dhcp6/Dhcp6AddrRegInformPacket.java
+++ b/staticlibs/device/com/android/net/module/util/dhcp6/Dhcp6AddrRegInformPacket.java
@@ -40,7 +40,7 @@
     /**
      * Generates a ADDR-REG-INFORM packet with the specified parameters.
      */
-    Dhcp6AddrRegInformPacket(int transId, int elapsedTime, @NonNull final byte[] clientDuid,
+    public Dhcp6AddrRegInformPacket(int transId, int elapsedTime, @NonNull final byte[] clientDuid,
             @NonNull final Inet6Address iaAddress, long preferred, long valid) {
         super(transId, elapsedTime, clientDuid, null /* serverDuid */, null /* iapd */);
         mIaAddress = iaAddress;
diff --git a/staticlibs/native/netjniutils/netjniutils.cpp b/staticlibs/native/netjniutils/netjniutils.cpp
index 8b7f903..ef49cf6 100644
--- a/staticlibs/native/netjniutils/netjniutils.cpp
+++ b/staticlibs/native/netjniutils/netjniutils.cpp
@@ -19,7 +19,7 @@
 
 #include <dlfcn.h>
 #include <stdbool.h>
-#include <string.h>
+#include <stdlib.h>
 #include <sys/system_properties.h>
 
 #include <android/api-level.h>
@@ -28,10 +28,7 @@
 namespace android {
 namespace netjniutils {
 
-namespace {
-
-
-int GetNativeFileDescriptorWithoutNdk(JNIEnv* env, jobject javaFd) {
+static int GetNativeFileDescriptorWithoutNdk(JNIEnv* env, jobject javaFd) {
   // Prior to Android S, we need to find the descriptor field in the FileDescriptor class. The
   // symbol name has been stable in libcore, but is a private implementation detail.
   // Older libnativehelper_compat_c++ versions had a jniGetFdFromFileDescriptor method, but this
@@ -43,14 +40,15 @@
     env->DeleteLocalRef(cls);
     if (fieldID == nullptr) {
       __android_log_print(ANDROID_LOG_FATAL, LOG_TAG, "Failed to get descriptor field.");
+      abort();
     }
     return fieldID;
   }();
 
-  return javaFd != nullptr ? env->GetIntField(javaFd, descriptorFieldID) : -1;
+  return env->GetIntField(javaFd, descriptorFieldID);
 }
 
-int GetNativeFileDescriptorWithNdk(JNIEnv* env, jobject javaFd) {
+static int GetNativeFileDescriptorWithNdk(JNIEnv* env, jobject javaFd) {
   // Since Android S, there is an NDK API to get a file descriptor present in libnativehelper.so.
   // libnativehelper is loaded into all processes by the zygote since the zygote uses it
   // to load the Android Runtime and is also a public library (because of the NDK API).
@@ -62,18 +60,17 @@
       __android_log_print(ANDROID_LOG_FATAL, LOG_TAG,
                           "Failed to dlsym(AFileDescriptor_getFd): %s", dlerror());
       dlclose(handle);
+      abort();
     }
     return ndkGetFd;
   }();
 
-  return javaFd != nullptr ? ndkGetFd(env, javaFd) : -1;
+  return ndkGetFd(env, javaFd);
 }
 
-}  //  namespace
-
 int GetNativeFileDescriptor(JNIEnv* env, jobject javaFd) {
-  static const bool preferNdkFileDescriptorApi = []() -> bool
-   { return android::modules::sdklevel::IsAtLeastS(); }();
+  if (!javaFd) return -1;
+  static const bool preferNdkFileDescriptorApi = modules::sdklevel::IsAtLeastS();
   if (preferNdkFileDescriptorApi) {
     return GetNativeFileDescriptorWithNdk(env, javaFd);
   } else {
diff --git a/staticlibs/testutils/devicetests/com/android/testutils/ContentResolverWithFakeSettingsProvider.kt b/staticlibs/testutils/devicetests/com/android/testutils/ContentResolverWithFakeSettingsProvider.kt
index 4eff040..21e239b 100644
--- a/staticlibs/testutils/devicetests/com/android/testutils/ContentResolverWithFakeSettingsProvider.kt
+++ b/staticlibs/testutils/devicetests/com/android/testutils/ContentResolverWithFakeSettingsProvider.kt
@@ -40,11 +40,13 @@
             val callbackUser = p.second
             val user = UserHandle(userId)
             val currentUser = UserHandle.getUserHandleForUid(Process.myUid())
-            if (callbackUser == UserHandle.ALL ||
-                UserHandle.CURRENT.equals(callbackUser) && currentUser.equals(user) ||
-                callbackUser.equals(user)
-            ) {
+            if (callbackUser == UserHandle.CURRENT && currentUser == user) {
+                // Use the overload without the user parameter since it works on S.
+                // TODO: when S is no longer supported, remove this and always use the branch below.
                 Log.d(TAG, "Notifying change in $uri")
+                observer.onChange(false, Collections.singletonList(uri), 0)
+            } else if (callbackUser == UserHandle.ALL || callbackUser.equals(user)) {
+                Log.d(TAG, "Notifying change in $uri on user $user")
                 observer.onChange(false, Collections.singletonList(uri), 0, user)
             }
         }))
diff --git a/tests/cts/net/src/android/net/cts/NetworkAgentTest.kt b/tests/cts/net/src/android/net/cts/NetworkAgentTest.kt
index b2d988f..777b55a 100644
--- a/tests/cts/net/src/android/net/cts/NetworkAgentTest.kt
+++ b/tests/cts/net/src/android/net/cts/NetworkAgentTest.kt
@@ -181,6 +181,8 @@
 
 private const val QUEUE_NETWORK_AGENT_EVENTS_IN_SYSTEM_SERVER =
     "queue_network_agent_events_in_system_server"
+private const val INGRESS_TO_VPN_ADDRESS_FILTERING =
+        "ingress_to_vpn_address_filtering"
 
 // When waiting for a NetworkCallback to determine there was no timeout, waiting is the
 // only possible thing (the relevant handler is the one in the real ConnectivityService,
@@ -2079,4 +2081,67 @@
         } while (SystemClock.elapsedRealtime() < deadline)
         fail("Binder Proxy is leaked: $startCount -> $endCount")
     }
+
+    fun doTestIngressToVpnAddressFiltering(vpnType: Int, expectFiltering: Boolean) {
+        assumeTrue(mCM.isConnectivityServiceFeatureEnabledForTesting(
+                INGRESS_TO_VPN_ADDRESS_FILTERING
+        ))
+
+        val ifname = createTunInterface(listOf(LINK_ADDRESS)).interfaceName
+        val lp = makeTestLinkProperties(ifname)
+        val nc = makeTestNetworkCapabilities(transports = intArrayOf(TRANSPORT_VPN)).apply {
+            setTransportInfo(VpnTransportInfo(
+                    vpnType,
+                    "MySession12345",
+                    /*bypassable=*/
+                    false,
+                    /*longLivedTcpConnectionsExpensive=*/
+                    false
+            ))
+        }
+        val agent = createNetworkAgent(initialNc = nc, initialLp = lp)
+        agent.register()
+        agent.markConnected()
+
+        val cb = TestableNetworkCallback()
+        registerNetworkCallback(makeTestNetworkRequest(), cb)
+        cb.eventuallyExpect<LinkPropertiesChanged> {
+            it.network == agent.network
+        }
+
+        val ifIndex = Os.if_nametoindex(ifname)
+        val ruleString = "[" + LINK_ADDRESS.address + "]: " + ifIndex + "(" + ifname + ")"
+        assertEquals(
+                expectFiltering,
+                "dumpsys connectivity trafficcontroller".execute().contains(ruleString)
+        )
+    }
+
+    @Test
+    fun testIngressToVpnAddressFiltering_VpnPlatform() {
+        doTestIngressToVpnAddressFiltering(
+            vpnType = VpnManager.TYPE_VPN_PLATFORM,
+            expectFiltering = true
+        )
+    }
+
+    @Test
+    fun testIngressToVpnAddressFiltering_VpnOem() {
+        // Ingress to VPN address filtering rule should not be added because OEM VPNs might need to
+        // receive packets to VPN address via non-VPN interface.
+        doTestIngressToVpnAddressFiltering(
+            vpnType = VpnManager.TYPE_VPN_OEM,
+            expectFiltering = false
+        )
+    }
+
+    @Test
+    fun testIngressToVpnAddressFiltering_VpnLegacy() {
+        // Ingress to VPN address filtering rule should not be added because legacy VPNs might need
+        // to receive packets to VPN address via non-VPN interface.
+        doTestIngressToVpnAddressFiltering(
+            vpnType = VpnManager.TYPE_VPN_LEGACY,
+            expectFiltering = false
+        )
+    }
 }
diff --git a/tests/unit/java/com/android/metrics/SatisfiedByLocalNetworkMetricsTest.kt b/tests/unit/java/com/android/metrics/SatisfiedByLocalNetworkMetricsTest.kt
index d664754..d6c378f 100644
--- a/tests/unit/java/com/android/metrics/SatisfiedByLocalNetworkMetricsTest.kt
+++ b/tests/unit/java/com/android/metrics/SatisfiedByLocalNetworkMetricsTest.kt
@@ -21,6 +21,7 @@
 import android.net.NetworkCapabilities
 import android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET
 import android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED
+import android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED
 import android.net.NetworkCapabilities.TRANSPORT_WIFI
 import android.net.NetworkRequest
 import android.net.NetworkRequest.Type
@@ -66,7 +67,9 @@
 
         // Simulate caps which can potentially be satisfied by non-thread
         // local networks.
+        // Note that requests built by NetworkRequest normally have deduced NOT_VCN_MANAGED.
         private val WIFI_CAPS = NetworkCapabilities.Builder()
+                .addCapability(NET_CAPABILITY_NOT_VCN_MANAGED)
                 .addCapability(NET_CAPABILITY_NOT_METERED)
                 .addTransportType(TRANSPORT_WIFI)
                 .build()
diff --git a/tests/unit/java/com/android/server/ConnectivityServiceTest.java b/tests/unit/java/com/android/server/ConnectivityServiceTest.java
index d88d15c..86e0c34 100644
--- a/tests/unit/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/unit/java/com/android/server/ConnectivityServiceTest.java
@@ -254,7 +254,6 @@
 import android.compat.testing.PlatformCompatChangeRule;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
-import android.content.ContentProvider;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
@@ -267,6 +266,7 @@
 import android.content.pm.ServiceInfo;
 import android.content.pm.UserInfo;
 import android.content.res.Resources;
+import android.database.ContentObserver;
 import android.location.LocationManager;
 import android.net.CaptivePortal;
 import android.net.CaptivePortalData;
@@ -373,7 +373,6 @@
 import android.telephony.TelephonyManager;
 import android.telephony.data.EpsBearerQosSessionAttributes;
 import android.telephony.data.NrQosSessionAttributes;
-import android.test.mock.MockContentResolver;
 import android.text.TextUtils;
 import android.util.ArraySet;
 import android.util.Log;
@@ -429,6 +428,7 @@
 import com.android.server.connectivity.TcpKeepaliveController;
 import com.android.server.connectivity.UidRangeUtils;
 import com.android.server.net.NetworkPinner;
+import com.android.testutils.ContentResolverWithFakeSettingsProvider;
 import com.android.testutils.DevSdkIgnoreRule;
 import com.android.testutils.DevSdkIgnoreRunner;
 import com.android.testutils.FunctionalUtils.Function3;
@@ -686,7 +686,7 @@
     }
 
     private class MockContext extends BroadcastInterceptingContext {
-        private final MockContentResolver mContentResolver;
+        private final ContentResolverWithFakeSettingsProvider mContentResolver;
 
         @Spy private Resources mInternalResources;
         private final LinkedBlockingQueue<Intent> mStartedActivities = new LinkedBlockingQueue<>();
@@ -703,7 +703,7 @@
             }).when(mInternalResources).getString(resId);
         }
 
-        MockContext(Context base, ContentProvider settingsProvider) {
+        MockContext(Context base) {
             super(base);
 
             mInternalResources = spy(base.getResources());
@@ -727,8 +727,7 @@
                 mockStringResource(resId);
             }
 
-            mContentResolver = new MockContentResolver();
-            mContentResolver.addProvider(Settings.AUTHORITY, settingsProvider);
+            mContentResolver = new ContentResolverWithFakeSettingsProvider();
         }
 
         @Override
@@ -1908,8 +1907,7 @@
         doReturn(mTelephonyManager).when(mTelephonyManager).createForSubscriptionId(anyInt());
 
         FakeSettingsProvider.clearSettingsProvider();
-        mServiceContext = new MockContext(InstrumentationRegistry.getContext(),
-                new FakeSettingsProvider());
+        mServiceContext = new MockContext(InstrumentationRegistry.getContext());
         mServiceContext.setUseRegisteredHandlers(true);
         mServiceContext.setPermission(NETWORK_FACTORY, PERMISSION_GRANTED);
         mServiceContext.setPermission(NETWORK_STACK, PERMISSION_GRANTED);
@@ -2032,6 +2030,13 @@
         }
 
         @Override
+        public void registerContentObserver(ContentResolver cr, Uri uri,
+                boolean notifyForDescendants, ContentObserver observer) {
+            ((ContentResolverWithFakeSettingsProvider) mServiceContext.getContentResolver())
+                    .registerContentObserver(uri, observer);
+        }
+
+        @Override
         public NetworkStackClientBase getNetworkStack() {
             return mNetworkStack;
         }
@@ -2237,6 +2242,7 @@
                 case ConnectivityFlags.EARLY_LINK_PROPERTIES_UPDATE_FOR_VPN:
                 case ConnectivityFlags.CONSTRAINED_DATA_SATELLITE_METRICS:
                 case ConnectivityFlags.SATISFIED_BY_LOCAL_NETWORK_METRICS:
+                case ConnectivityFlags.USE_SATELLITE_REPORTED_SUSPENDED_AND_ROAMING:
                     return true;
                 default:
                     throw new UnsupportedOperationException("Unknown flag " + name
@@ -5889,21 +5895,18 @@
         ContentResolver cr = mServiceContext.getContentResolver();
         Settings.Global.putInt(cr, ConnectivitySettingsManager.MOBILE_DATA_ALWAYS_ON,
                 enable ? 1 : 0);
-        mService.updateAlwaysOnNetworks();
         waitForIdle();
     }
 
     private void setPrivateDnsSettings(int mode, String specifier) {
         ConnectivitySettingsManager.setPrivateDnsMode(mServiceContext, mode);
         ConnectivitySettingsManager.setPrivateDnsHostname(mServiceContext, specifier);
-        mService.updatePrivateDnsSettings();
         waitForIdle();
     }
 
     private void setIngressRateLimit(int rateLimitInBytesPerSec) {
         ConnectivitySettingsManager.setIngressRateLimitInBytesPerSecond(mServiceContext,
                 rateLimitInBytesPerSec);
-        mService.updateIngressRateLimit();
         waitForIdle();
     }
 
@@ -18119,7 +18122,6 @@
 
     private void setAndUpdateMobileDataPreferredUids(Set<Integer> uids) {
         ConnectivitySettingsManager.setMobileDataPreferredUids(mServiceContext, uids);
-        mService.updateMobileDataPreferredUids();
         waitForIdle();
     }
 
@@ -18323,19 +18325,17 @@
      */
     @Test
     public void testMobileDataPreferredUidsChangedCountsRequestsCorrectlyOnSet() throws Exception {
-        ConnectivitySettingsManager.setMobileDataPreferredUids(mServiceContext,
-                Set.of(PRIMARY_USER_HANDLE.getUid(TEST_PACKAGE_UID)));
+        Set uids = Set.of(PRIMARY_USER_HANDLE.getUid(TEST_PACKAGE_UID));
+        ConnectivitySettingsManager.setMobileDataPreferredUids(mServiceContext, uids);
         // Leave one request available so MDO preference set up above can be set.
         withRequestCountersAcquired(1 /* countToLeaveAvailable */, () ->
                 withPermission(NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
                         Process.myPid(), Process.myUid(), () -> {
                             // Set initially to test the limit prior to having existing requests.
-                            mService.updateMobileDataPreferredUids();
-                            waitForIdle();
+                            setAndUpdateMobileDataPreferredUids(uids);
 
                             // re-set so as to test the limit as part of replacing existing requests
-                            mService.updateMobileDataPreferredUids();
-                            waitForIdle();
+                            setAndUpdateMobileDataPreferredUids(uids);
                         }));
     }
 
diff --git a/tests/unit/java/com/android/server/connectivityservice/CSBlockedReasonsTest.kt b/tests/unit/java/com/android/server/connectivityservice/CSBlockedReasonsTest.kt
index c33dc69..707df7f 100644
--- a/tests/unit/java/com/android/server/connectivityservice/CSBlockedReasonsTest.kt
+++ b/tests/unit/java/com/android/server/connectivityservice/CSBlockedReasonsTest.kt
@@ -336,7 +336,6 @@
 
         // CS must send correct blocked reasons after per app default network change
         ConnectivitySettingsManager.setMobileDataPreferredUids(context, setOf(Process.myUid()))
-        service.updateMobileDataPreferredUids()
         cb.expectAvailableCallbacks(
                 cellAgent.network,
                 validated = false,
@@ -345,7 +344,6 @@
 
         // Remove per app default network request
         ConnectivitySettingsManager.setMobileDataPreferredUids(context, setOf())
-        service.updateMobileDataPreferredUids()
         cb.expectAvailableCallbacks(
                 wifiAgent.network,
                 validated = false,
diff --git a/tests/unit/java/com/android/server/connectivityservice/CSQueuedCallbacksTest.kt b/tests/unit/java/com/android/server/connectivityservice/CSQueuedCallbacksTest.kt
index 4f15b1a..e96a3ef 100644
--- a/tests/unit/java/com/android/server/connectivityservice/CSQueuedCallbacksTest.kt
+++ b/tests/unit/java/com/android/server/connectivityservice/CSQueuedCallbacksTest.kt
@@ -592,7 +592,6 @@
 
     private fun setMobileDataPreferredUids(uids: Set<Int>) {
         ConnectivitySettingsManager.setMobileDataPreferredUids(context, uids)
-        service.updateMobileDataPreferredUids()
         waitForIdle()
     }
 }
diff --git a/tests/unit/java/com/android/server/connectivityservice/CSSatelliteNetworkTest.kt b/tests/unit/java/com/android/server/connectivityservice/CSSatelliteNetworkTest.kt
index 31f70fd..5e2cfa0 100644
--- a/tests/unit/java/com/android/server/connectivityservice/CSSatelliteNetworkTest.kt
+++ b/tests/unit/java/com/android/server/connectivityservice/CSSatelliteNetworkTest.kt
@@ -48,8 +48,11 @@
 import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo
 import com.android.testutils.DevSdkIgnoreRunner
 import com.android.testutils.TestableNetworkCallback
+import com.android.testutils.TestableNetworkCallback.Event.CapabilitiesChanged
 import com.android.testutils.TestableNetworkCallback.Event.Losing
 import com.android.testutils.TestableNetworkCallback.Event.Lost
+import com.android.testutils.TestableNetworkCallback.Event.Resumed
+import com.android.testutils.TestableNetworkCallback.Event.Suspended
 import com.android.testutils.runAsShell
 import com.android.testutils.visibleOnHandlerThread
 import kotlin.test.assertEquals
@@ -301,6 +304,46 @@
         otherUidCb.assertNoCallback()
     }
 
+    @Test
+    fun testSuspendAndRoam() {
+        val agent = createSatelliteAgent(
+                name = "satellite0",
+                restricted = false,
+                keepConnected = true
+        )
+        agent.connect()
+        val nr = NetworkRequest.Builder()
+                .clearCapabilities()
+                .addTransportType(TRANSPORT_SATELLITE)
+                .build()
+        val cb = TestableNetworkCallback()
+        cm.registerNetworkCallback(nr, cb)
+        cb.eventuallyExpect<CapabilitiesChanged> {it.network == agent.network &&
+                    it.caps.hasCapability(NET_CAPABILITY_NOT_SUSPENDED) &&
+                    it.caps.hasCapability(NET_CAPABILITY_NOT_ROAMING)
+        }
+
+        // Suspend satellite network
+        val nc1 = satelliteNc(restricted = false)
+                .removeCapability(NET_CAPABILITY_NOT_SUSPENDED)
+                .removeCapability(NET_CAPABILITY_NOT_ROAMING)
+        agent.sendNetworkCapabilities(nc1)
+        cb.eventuallyExpect<CapabilitiesChanged> {it.network == agent.network &&
+                    !it.caps.hasCapability(NET_CAPABILITY_NOT_SUSPENDED) &&
+                    !it.caps.hasCapability(NET_CAPABILITY_NOT_ROAMING)
+        }
+        cb.expect<Suspended>(agent)
+
+        // Resume satellite network
+        val nc2 = satelliteNc(restricted = false)
+        agent.sendNetworkCapabilities(nc2)
+        cb.expect<CapabilitiesChanged> {it.network == agent.network &&
+                it.caps.hasCapability(NET_CAPABILITY_NOT_SUSPENDED) &&
+                it.caps.hasCapability(NET_CAPABILITY_NOT_ROAMING)
+        }
+        cb.expect<Resumed>(agent)
+    }
+
     private fun assertCreateMultiLayerNrisFromSatelliteNetworkPreferredUids(uids: Set<Int>) {
         val nris =
             service.createMultiLayerNrisFromSatelliteNetworkFallbackUids(uids, emptySet())
diff --git a/tests/unit/java/com/android/server/connectivityservice/base/CSTest.kt b/tests/unit/java/com/android/server/connectivityservice/base/CSTest.kt
index 5cb2442..68b05dc 100644
--- a/tests/unit/java/com/android/server/connectivityservice/base/CSTest.kt
+++ b/tests/unit/java/com/android/server/connectivityservice/base/CSTest.kt
@@ -21,12 +21,14 @@
 import android.app.AppOpsManager
 import android.bluetooth.BluetoothManager
 import android.content.BroadcastReceiver
+import android.content.ContentResolver
 import android.content.Context
 import android.content.Intent
 import android.content.IntentFilter
 import android.content.pm.PackageManager.PERMISSION_GRANTED
 import android.content.pm.UserInfo
 import android.content.res.Resources
+import android.database.ContentObserver
 import android.net.ConnectivityManager
 import android.net.IDnsResolver
 import android.net.INetd
@@ -48,6 +50,7 @@
 import android.net.NetworkScore
 import android.net.NetworkScore.KEEP_CONNECTED_FOR_TEST
 import android.net.PacProxyManager
+import android.net.Uri
 import android.net.connectivity.ConnectivityCompatChanges.ENABLE_MATCH_LOCAL_NETWORK
 import android.net.networkstack.NetworkStackClientBase
 import android.os.BatteryStatsManager
@@ -66,6 +69,7 @@
 import androidx.test.platform.app.InstrumentationRegistry
 import com.android.internal.app.IBatteryStats
 import com.android.internal.util.test.BroadcastInterceptingContext
+import com.android.internal.util.test.FakeSettingsProvider
 import com.android.metrics.DefaultNetworkRematchMetrics
 import com.android.metrics.SatelliteCoarseUsageMetricsCollector
 import com.android.metrics.SatisfiedByLocalNetworkMetrics
@@ -88,6 +92,7 @@
 import com.android.server.connectivity.ProxyTracker
 import com.android.server.connectivity.QuicConnectionCloser
 import com.android.server.connectivity.SatelliteAccessController
+import com.android.testutils.ContentResolverWithFakeSettingsProvider
 import com.android.testutils.visibleOnHandlerThread
 import com.android.testutils.waitForIdle
 import java.net.InetAddress
@@ -168,7 +173,11 @@
 
     val instrumentationContext =
             TestableContext(InstrumentationRegistry.getInstrumentation().context)
-    val context = CSContext(instrumentationContext)
+    val context = CSContext(instrumentationContext).also {
+        // TestableContext uses its own fake settings provider. Reset it so that
+        // the code uses the ContentResolverWithFakeSettingsProvider initialized later.
+        FakeSettingsProvider.clearSettingsProvider()
+    }
 
     // See constructor for default-enabled features. All queried features must be either enabled
     // or disabled, because the test can't hold READ_DEVICE_CONFIG and device config utils query
@@ -189,13 +198,14 @@
         it[ConnectivityFlags.EARLY_LINK_PROPERTIES_UPDATE_FOR_VPN] = true
         it[ConnectivityFlags.CONSTRAINED_DATA_SATELLITE_METRICS] = true
         it[ConnectivityFlags.SATISFIED_BY_LOCAL_NETWORK_METRICS] = true
+        it[ConnectivityFlags.USE_SATELLITE_REPORTED_SUSPENDED_AND_ROAMING] = true
     }
     fun setFeatureEnabled(flag: String, enabled: Boolean) = enabledFeatures.set(flag, enabled)
 
     // When adding new members, consider if it's not better to build the object in CSTestHelpers
     // to keep this file clean of implementation details. Generally, CSTestHelpers should only
     // need changes when new details of instrumentation are needed.
-    val contentResolver = makeMockContentResolver(context)
+    val contentResolver = ContentResolverWithFakeSettingsProvider()
 
     val PRIMARY_USER = 0
     val PRIMARY_USER_INFO = UserInfo(
@@ -340,6 +350,14 @@
         override fun makeMulticastRoutingCoordinatorService(handler: Handler) =
                 this@CSTest.multicastRoutingCoordinatorService
 
+        override fun registerContentObserver(
+            cr: ContentResolver,
+            uri: Uri,
+            notifyForDescendants: Boolean,
+            observer: ContentObserver
+        ) =
+            (cr as ContentResolverWithFakeSettingsProvider).registerContentObserver(uri, observer)
+
         override fun makeCarrierPrivilegeAuthenticator(
                 context: Context,
                 tm: TelephonyManager,
diff --git a/tests/unit/java/com/android/server/connectivityservice/base/CSTestHelpers.kt b/tests/unit/java/com/android/server/connectivityservice/base/CSTestHelpers.kt
index 69ff193..b3dada5 100644
--- a/tests/unit/java/com/android/server/connectivityservice/base/CSTestHelpers.kt
+++ b/tests/unit/java/com/android/server/connectivityservice/base/CSTestHelpers.kt
@@ -47,11 +47,8 @@
 import android.os.SystemConfigManager
 import android.os.UserHandle
 import android.os.UserManager
-import android.provider.Settings
-import android.test.mock.MockContentResolver
 import com.android.connectivity.resources.R
 import com.android.internal.util.WakeupMessage
-import com.android.internal.util.test.FakeSettingsProvider
 import com.android.modules.utils.build.SdkLevel
 import com.android.server.ConnectivityService.Dependencies
 import com.android.server.connectivity.ConnectivityResources
@@ -103,10 +100,6 @@
     addRoute(RouteInfo(IpPrefix("0.0.0.0/0"), null, null))
 }
 
-internal fun makeMockContentResolver(context: Context) = MockContentResolver(context).apply {
-    addProvider(Settings.AUTHORITY, FakeSettingsProvider())
-}
-
 internal fun makeMockUserManager(info: UserInfo, handle: UserHandle) = mock<UserManager>().also {
     doReturn(listOf(info)).`when`(it).getAliveUsers()
     doReturn(listOf(handle)).`when`(it).getUserHandles(ArgumentMatchers.anyBoolean())