CVE-2024-49734 AI-generated analysis
platform/packages/modules/Connectivity (ConnectivityService, BpfNetMaps ingress discard rules) and platform/frameworks/libs/net (Struct, InetAddressUtils)
ConnectivityService now installs ingress discard rules (via mBpfNetMaps.setIngressDiscardRule) so that packets to an address used only by a VPN interface are dropped when they arrive on a non-VPN interface. Two frameworks/libs/net changes accompany it and keep IPv4-mapped IPv6 addresses as Inet6Address.
Root cause
Before the fix, ConnectivityService had no way to reject traffic to a VPN interface's addresses when it arrived on other interfaces. updateLinkProperties called only updateVpnFiltering, and the destroy path had no related cleanup. So a packet sent to the VPN-assigned address could be accepted through a non-VPN interface. Separately, Struct.java parsed Type.Ipv6Address fields with InetAddress.getByAddress, which turns a v4-mapped v6 byte array into an Inet4Address.
How the fix works
- frameworks/libs/net b20d67cbfc24, common/framework/com/android/net/module/util/InetAddressUtils.java: adds InetAddressUtils.v4MappedV6Address(Inet4Address). It builds a 16-byte ::ffff:a.b.c.d array and returns it through Inet6Address.getByAddress(null, bytes, -1), so the result is not collapsed back to Inet4Address. On the (unreachable) UnknownHostException it logs wtf and returns null.
- frameworks/libs/net e72c61380c52, common/device/com/android/net/module/util/Struct.java (field read switch, case Ipv4Address/Ipv6Address): when isIpv6 is true, it uses Inet6Address.getByAddress(null, address, -1) instead of InetAddress.getByAddress, so v4-mapped v6 addresses parse as Inet6Address. IPv4 fields are unchanged.
- packages/modules/Connectivity 1a0dd8ccde71, service/src/com/android/server/ConnectivityService.java: adds the field mIngressToVpnAddressFiltering, set in the constructor to mDeps.isAtLeastT(). The commit message says isTetheringFeatureNotChickenedOut is not available on U, so the feature is enabled on T+ with no kill switch.
- ConnectivityService.generateIngressDiscardRules(nai, lp): first counts how many non-destroyed NetworkAgentInfos own each address from LinkProperties.getAllAddresses(). The set is mNetworkAgentInfos plus the passed nai, and the passed lp is used for nai. It then goes through every non-destroyed VPN agent that has a non-null interface name. For each address with an owner count of exactly 1 that is not link-local, it emits Pair<addr, ifname>.
- ConnectivityService.updateIngressToVpnAddressFiltering(newLp, oldLp, nai): returns early if the flag is off or !mDeps.isAtLeastT(). Otherwise it diffs the rules generated from oldLp and newLp with CompareOrUpdateResult, keyed on the address. It calls mBpfNetMaps.removeIngressDiscardRule(addr) for removed rules and mBpfNetMaps.setIngressDiscardRule(addr, ifname) for added and updated rules.
- Call sites: updateLinkProperties calls updateIngressToVpnAddressFiltering(newLp, oldLp, networkAgent) right after updateVpnFiltering. The network destroy path calls updateIngressToVpnAddressFiltering(null, nai.linkProperties, nai) just before mNetd.networkDestroy.
- Follow-up c16addaec604 (android-security-14.0.0_r16), ConnectivityService.generateIngressDiscardRules: also skips agents where getVpnType(agent) == VpnManager.TYPE_VPN_LEGACY, because some legacy VPNs need to receive packets to the VPN address through a non-VPN interface.
Completeness
- Covers all addresses from LinkProperties.getAllAddresses() of non-destroyed, non-legacy VPN agents. Rules are re-evaluated across all networks on each updateLinkProperties call and on network destroy.
- Adds no rules for addresses configured on more than one interface (owner count != 1), for link-local addresses, or, after c16addaec604, for legacy VPNs (VpnManager.TYPE_VPN_LEGACY).
- Rules are keyed by address only (the CompareOrUpdateResult key is rule.first). When the owning interface changes, setIngressDiscardRule overwrites the rule.
- Enabled only when mDeps.isAtLeastT(); this branch has no kill switch. The material does not include the BpfNetMaps.setIngressDiscardRule/removeIngressDiscardRule implementation or the enforcement path.
- The Struct.java change affects only Type.Ipv6Address fields. The new InetAddressUtils.v4MappedV6Address helper is added, but the material does not show its caller.
Behavior changes
- Packets sent to an address unique to a non-legacy VPN interface are covered by an ingress discard rule bound to that interface, which is meant to drop them when they come in on other interfaces.
- Ingress discard rules are added, updated and removed as VPN LinkProperties change, and are processed for removal when the network is destroyed.
- Struct parsing of Type.Ipv6Address fields now always yields Inet6Address, including for v4-mapped v6 byte patterns that used to become Inet4Address.
- The follow-up commit excludes legacy VPNs (TYPE_VPN_LEGACY) from the new filtering.
Per major
- Android 14: Adapted fix. Cherry-picks of aosp/2795709, aosp/2807798 and aosp/2795711 went into frameworks/libs/net and packages/modules/Connectivity in android-security-14.0.0_r16 for non-mainline U devices. Tests were dropped, and the isTetheringFeatureNotChickenedOut kill switch was replaced by an unconditional isAtLeastT() enable. Follow-up c16addaec604 in the same tag skips legacy VPNs. At android-security-14.0.0_r29, 6/8 lines of the patched region are present (fix recorded as c16addaec604).
- Android 15: Upstream fix: the change "Drop packets to VPN address ingressing via non-VPN interface" was merged into main and is in android-15.0.0_r1 (merge c4c1c3721c0a). The state of the patched region at android-security-15.0.0_r17 is unknown from the material.
- Android 16: Not affected per the bulletin; the project is not present in this major's security tags.
- Android 17: Not affected per the bulletin; the project is not present in this major's security tags.
Unknowns
- How BpfNetMaps.setIngressDiscardRule/removeIngressDiscardRule and the ingress enforcement are implemented, including whether they use the new v4MappedV6Address helper or the Struct Ipv6Address handling.
- Whether the ConnectivityService change depends on the two libs/net commits, and in what order the commits must be applied.
- What information is disclosed and under which network topology; the bulletin gives only the ID type and the High rating.
- Which 2 of the 8 patched-region lines differ at android-security-14.0.0_r29.
- Whether the patched region on Android 15 (android-security-15.0.0_r17) matches the Android 14 form, including the legacy VPN exclusion.