@@ -936,6 +936,9 @@ private static IDnsResolver getDnsResolver(Context context) {
936936 private final Map<String, ApplicationSelfCertifiedNetworkCapabilities>
937937 mSelfCertifiedCapabilityCache = new HashMap<>();
938938
939+ // Flag to drop packets to VPN addresses ingressing via non-VPN interfaces.
940+ private final boolean mIngressToVpnAddressFiltering;
941+
939942 /**
940943 * Implements support for the legacy "one network per network type" model.
941944 *
@@ -1798,6 +1801,7 @@ public void onUidFrozenStateChanged(int[] uids, int[] frozenStates) {
17981801 activityManager.registerUidFrozenStateChangedCallback(
17991802 (Runnable r) -> r.run(), frozenStateChangedCallback);
18001803 }
1804+ mIngressToVpnAddressFiltering = mDeps.isAtLeastT();
18011805 }
18021806
18031807 /**
@@ -4776,6 +4780,7 @@ private void destroyNativeNetwork(@NonNull NetworkAgentInfo nai) {
47764780 if (mDscpPolicyTracker != null) {
47774781 mDscpPolicyTracker.removeAllDscpPolicies(nai, false);
47784782 }
4783+ updateIngressToVpnAddressFiltering(null, nai.linkProperties, nai);
47794784 try {
47804785 mNetd.networkDestroy(nai.network.getNetId());
47814786 } catch (RemoteException | ServiceSpecificException e) {
@@ -8014,6 +8019,8 @@ private void updateLinkProperties(NetworkAgentInfo networkAgent, @NonNull LinkPr
80148019 // new interface (the interface name -> index map becomes initialized)
80158020 updateVpnFiltering(newLp, oldLp, networkAgent);
80168021
8022+ updateIngressToVpnAddressFiltering(newLp, oldLp, networkAgent);
8023+
80178024 updateMtu(newLp, oldLp);
80188025 // TODO - figure out what to do for clat
80198026// for (LinkProperties lp : newLp.getStackedLinks()) {
@@ -8337,6 +8344,87 @@ private void updateVpnFiltering(@NonNull LinkProperties newLp, @Nullable LinkPro
83378344 }
83388345 }
83398346
8347+ /**
8348+ * Returns ingress discard rules to drop packets to VPN addresses ingressing via non-VPN
8349+ * interfaces.
8350+ * Ingress discard rule is added to the address iff
8351+ * 1. The address is not a link local address
8352+ * 2. The address is used by a single VPN interface and not used by any other
8353+ * interfaces even non-VPN ones
8354+ * This method can be called during network disconnects, when nai has already been removed from
8355+ * mNetworkAgentInfos.
8356+ *
8357+ * @param nai This method generates rules assuming lp of this nai is the lp at the second
8358+ * argument.
8359+ * @param lp This method generates rules assuming lp of nai at the first argument is this lp.
8360+ * Caller passes old lp to generate old rules and new lp to generate new rules.
8361+ * @return ingress discard rules. Set of pairs of addresses and interface names
8362+ */
8363+ private Set<Pair<InetAddress, String>> generateIngressDiscardRules(
8364+ @NonNull final NetworkAgentInfo nai, @Nullable final LinkProperties lp) {
8365+ Set<NetworkAgentInfo> nais = new ArraySet<>(mNetworkAgentInfos);
8366+ nais.add(nai);
8367+ // Determine how many networks each IP address is currently configured on.
8368+ // Ingress rules are added only for IP addresses that are configured on single interface.
8369+ final Map<InetAddress, Integer> addressOwnerCounts = new ArrayMap<>();
8370+ for (final NetworkAgentInfo agent : nais) {
8371+ if (agent.isDestroyed()) {
8372+ continue;
8373+ }
8374+ final LinkProperties agentLp = (nai == agent) ? lp : agent.linkProperties;
8375+ if (agentLp == null) {
8376+ continue;
8377+ }
8378+ for (final InetAddress addr: agentLp.getAllAddresses()) {
8379+ addressOwnerCounts.put(addr, addressOwnerCounts.getOrDefault(addr, 0) + 1);
8380+ }
8381+ }
8382+
8383+ // Iterates all networks instead of only generating rule for nai that was passed in since
8384+ // lp of the nai change could cause/resolve address collision and result in affecting rule
8385+ // for different network.
8386+ final Set<Pair<InetAddress, String>> ingressDiscardRules = new ArraySet<>();
8387+ for (final NetworkAgentInfo agent : nais) {
8388+ if (!agent.isVPN() || agent.isDestroyed()) {
8389+ continue;
8390+ }
8391+ final LinkProperties agentLp = (nai == agent) ? lp : agent.linkProperties;
8392+ if (agentLp == null || agentLp.getInterfaceName() == null) {
8393+ continue;
8394+ }
8395+
8396+ for (final InetAddress addr: agentLp.getAllAddresses()) {
8397+ if (addressOwnerCounts.get(addr) == 1 && !addr.isLinkLocalAddress()) {
8398+ ingressDiscardRules.add(new Pair<>(addr, agentLp.getInterfaceName()));
8399+ }
8400+ }
8401+ }
8402+ return ingressDiscardRules;
8403+ }
8404+
8405+ private void updateIngressToVpnAddressFiltering(@Nullable LinkProperties newLp,
8406+ @Nullable LinkProperties oldLp, @NonNull NetworkAgentInfo nai) {
8407+ // Having isAtleastT to avoid NewApi linter error (b/303382209)
8408+ if (!mIngressToVpnAddressFiltering || !mDeps.isAtLeastT()) {
8409+ return;
8410+ }
8411+ final CompareOrUpdateResult<InetAddress, Pair<InetAddress, String>> ruleDiff =
8412+ new CompareOrUpdateResult<>(
8413+ generateIngressDiscardRules(nai, oldLp),
8414+ generateIngressDiscardRules(nai, newLp),
8415+ (rule) -> rule.first);
8416+ for (Pair<InetAddress, String> rule: ruleDiff.removed) {
8417+ mBpfNetMaps.removeIngressDiscardRule(rule.first);
8418+ }
8419+ for (Pair<InetAddress, String> rule: ruleDiff.added) {
8420+ mBpfNetMaps.setIngressDiscardRule(rule.first, rule.second);
8421+ }
8422+ // setIngressDiscardRule overrides the existing rule
8423+ for (Pair<InetAddress, String> rule: ruleDiff.updated) {
8424+ mBpfNetMaps.setIngressDiscardRule(rule.first, rule.second);
8425+ }
8426+ }
8427+
83408428 private void updateWakeOnLan(@NonNull LinkProperties lp) {
83418429 if (mWolSupportedInterfaces == null) {
83428430 mWolSupportedInterfaces = new ArraySet<>(mResources.get().getStringArray(
0 commit comments