Skip to content

fix(android): recover default-interface index via Os.if_nametoindex#8889

Draft
garmr-ulfr wants to merge 3 commits into
mainfrom
garmr/fix/android-default-interface-index
Draft

fix(android): recover default-interface index via Os.if_nametoindex#8889
garmr-ulfr wants to merge 3 commits into
mainfrom
garmr/fix/android-default-interface-index

Conversation

@garmr-ulfr

@garmr-ulfr garmr-ulfr commented Jun 26, 2026

Copy link
Copy Markdown
Contributor

Problem

On Android, every sing-box outbound (direct, the auto-selector, and proxy outbounds) and DNS failed with no available network interface, while the UI still reported the VPN as Connected. Observed on Android 10 / API 29 (Samsung SM-G960U1); the condition persisted for the whole session with zero successful connections, and the auto-selector churned through servers marking each hard_demoted — a consequence, not the cause.

Root cause

DefaultNetworkMonitor.checkDefaultInterfaceUpdate is the only path that delivers the default network interface to sing-box (via updateDefaultInterface). It resolved the interface index solely through NetworkInterface.getByName(name).index, which relies on the interface-enumeration syscall. That syscall is restricted on some Android versions/ROMs, where getByName returns null or throws. On failure the 10-retry loop exhausted and returned without ever calling updateDefaultInterface, leaving sing-box with no default interface — so every outbound failed to bind.

The sibling PlatformInterfaceWrapper.getInterfaces() was already hardened for this exact case with an Os.if_nametoindex fallback; checkDefaultInterfaceUpdate was not.

Fix

Fall back to Os.if_nametoindex(interfaceName) when getByName cannot resolve the index, mirroring the hardened sibling, and retry only when the index is still unresolvable (<= 0). Also early-return on a null interface name instead of burning the retry budget on getByName(null).

Testing

Needs a build + on-device check on an API 29 device to confirm.

Summary by CodeRabbit

  • Bug Fixes
    • Improved handling of default network changes to more reliably detect the active interface.
    • Added a fallback method for resolving interface details when standard lookup fails.
    • Made interface tracking more resilient during retries, helping reduce missed or stale network updates.

NetworkInterface.getByName relies on the interface-enumeration syscall,
which is restricted on some Android versions/ROMs (returns null or throws
there). When it failed, checkDefaultInterfaceUpdate exhausted its retries
without ever calling updateDefaultInterface, so sing-box never received a
default interface and every outbound failed with "no available network
interface".

Fall back to Os.if_nametoindex when getByName cannot resolve the index,
mirroring the already-hardened getInterfaces sibling, and retry only when
the index is still unresolvable. Also early-return on a null interface
name instead of burning the retry budget on getByName(null).
@coderabbitai

coderabbitai Bot commented Jun 26, 2026

Copy link
Copy Markdown

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: bd5efc79-ce6b-4866-95d9-65985c36ac26

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • ✅ Review completed - (🔄 Check again to review again)
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch garmr/fix/android-default-interface-index

Comment @coderabbitai help to get the list of available commands.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR addresses an Android-specific failure mode where sing-box never receives a default network interface index, causing all outbounds/DNS to fail with “no available network interface” even while the VPN appears connected. It hardens DefaultNetworkMonitor.checkDefaultInterfaceUpdate to recover the interface index on restricted Android ROMs by falling back to Os.if_nametoindex.

Changes:

  • Add Os.if_nametoindex(interfaceName) fallback when NetworkInterface.getByName cannot resolve an interface index.
  • Avoid retrying when the interface name is null by early-returning before entering the retry loop.
  • Retry only when the resolved index is still not usable (<= 0).

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

garmr-ulfr and others added 2 commits June 26, 2026 13:35
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
getLinkProperties (and its interfaceName) can briefly return null right
after a network change. Resolving it once before the loop meant a transient
null abandoned the update entirely, leaving the default interface unset for
that network change. Resolve inside the retry loop instead, matching
upstream, so a transient null is retried rather than dropped.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants