Skip to content

Honor interface/IP filters for srflx and relay STUN requests - #899

Merged
sirzooro merged 1 commit into
pion:masterfrom
sirzooro:filter_stun_src_addr
Mar 15, 2026
Merged

sirzooro merged 1 commit into
pion:masterfrom
sirzooro:filter_stun_src_addr

Conversation

@sirzooro

@sirzooro sirzooro commented Mar 14, 2026 •

Copy link
Copy Markdown
Contributor

Summary

This PR fixes a bug where server-reflexive (srflx) STUN requests and TURN relay UDP sockets could be sent from interfaces that were filtered out by InterfaceFilter / IPFilter.

Previously:

  • srflx gathering used wildcard local binds (0.0.0.0 / ::)
  • relay TURN/UDP gathering used wildcard local bind (0.0.0.0:0)

That allowed kernel routing to choose source interfaces outside configured filters, which could expose public IPs from non-allowed adapters.

With this change, when interface/IP filters are configured:

  • srflx gathering binds STUN sockets to filtered local interface addresses only
  • relay (TURN/UDP) gathering binds local sockets to filtered local interface addresses only

Root cause

  • Wildcard local binds do not constrain source interface selection.
  • localInterfaces(...) filtering was applied to host gathering but not consistently to srflx and relay local bind paths.

What changed

  • gather.go

    • Refactored gatherCandidatesSrflx to support two modes:
      • Filtered mode (interfaceFilter or ipFilter set):
        • Resolve filtered local interfaces via localInterfaces(...).
        • Bind srflx sockets per matching local address/family.
      • Default mode (no filters):
        • Preserve existing wildcard-bind behavior.
    • Updated gatherCandidatesRelay (TURN/UDP path) with the same two-mode behavior:
      • Filtered mode binds relay UDP sockets per filtered local IPv4 address.
      • Default mode preserves legacy 0.0.0.0:0 bind behavior.
  • gather_test.go

    • Added srflxListenCaptureNet and TestGatherCandidatesSrflxRespectsInterfaceFilter.
    • Added relayListenCaptureNet and TestGatherCandidatesRelayRespectsInterfaceFilter.
    • Kept existing relay behavior checks via TestGatherCandidatesRelayCallsAddRelayCandidates.

Backward compatibility

  • No behavior change for users who do not configure InterfaceFilter / IPFilter.
  • When filters are configured, srflx/relay local bind behavior now consistently honors those filters.

Reference issue

Fixes #727

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Fixes srflx candidate gathering so STUN requests honor InterfaceFilter / IPFilter by binding srflx sockets to the filtered local interface addresses (instead of wildcard binds that allow kernel routing to choose disallowed interfaces).

Changes:

  • Refactors gatherCandidatesSrflx to use per-interface/per-IP binds when interface/IP filters are configured, while preserving wildcard bind behavior when no filters are set.
  • Adds a regression test that captures ListenUDP bind addresses and verifies only the allowed interface IP is used for srflx gathering.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.

File Description
gather.go Applies interface/IP filtering to srflx STUN socket binding to prevent leaking candidates from disallowed interfaces.
gather_test.go Adds a test net shim to capture srflx bind addresses and a regression test for InterfaceFilter behavior.

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

@sirzooro
sirzooro requested a review from JoTurk March 14, 2026 08:08
@codecov

codecov Bot commented Mar 14, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 72.56637% with 62 lines in your changes missing coverage. Please review.
✅ Project coverage is 88.35%. Comparing base (398ac7c) to head (0c217ac).
⚠️ Report is 1 commits behind head on master.

Files with missing lines Patch % Lines
gather.go 72.56% 42 Missing and 20 partials ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master     #899      +/-   ##
==========================================
- Coverage   88.37%   88.35%   -0.02%     
==========================================
  Files          44       44              
  Lines        5591     5626      +35     
==========================================
+ Hits         4941     4971      +30     
- Misses        451      452       +1     
- Partials      199      203       +4     
Flag Coverage Δ
go 88.35% <72.56%> (-0.02%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@sirzooro
sirzooro force-pushed the filter_stun_src_addr branch from 2be2a19 to b4bda32 Compare March 14, 2026 09:35
@sirzooro sirzooro changed the title Honor interface/IP filters for srflx STUN requests Honor interface/IP filters for srflx and relay STUN requests Mar 14, 2026
@sirzooro
sirzooro force-pushed the filter_stun_src_addr branch from b4bda32 to 0c217ac Compare March 14, 2026 09:42

@JoTurk JoTurk left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Thank you, I wanted to do this for a while now, thank you for picking it.

Comment thread agent_options_test.go
@sirzooro
sirzooro merged commit 8f6a882 into pion:master Mar 15, 2026
26 of 27 checks passed
@sirzooro
sirzooro deleted the filter_stun_src_addr branch March 15, 2026 06:05
@uumaro

uumaro commented Sep 28, 2026

Copy link
Copy Markdown

This change caused an unexpected change in behavior for us in Snowflake. We had been using SetIPFilter to strip LAN and localhost candidates (which are not needed in our application) from SDP, by default, for privacy reasons:

https://gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake/-/blob/v2.14.1/client/lib/webrtc.go#L274-283

if !keepLocalAddresses {
	s.SetIPFilter(func(ip net.IP) (keep bool) {
		keep = !util.IsLocal(ip) && !ip.IsLoopback() && !ip.IsUnspecified()
		return
	})
	s.SetICEMulticastDNSMode(ice.MulticastDNSModeDisabled)
}

After upgrading pion/ice beyond v4.2.2, peers that are behind NAT (which is most of them) stopped sending STUN requests. We traced the cause: the peer want to know its own external IP address outside the NAT, which it can do by means of a STUN server; but in order to send the STUN request, it needs to bind to a LAN IP address from inside the NAT, which is now prohibited by a SetIPFilter that filters out LAN addresses.

This isn't asking for any action from you, just FYI that this change had a side effect we did not expect. As a workaround, we've gone back to editing the text of SDP to filter out LAN and localhost candidates. This is the change where we originally migrated to SetIPFilter.

@JoTurk

JoTurk commented Sep 28, 2026 •

Copy link
Copy Markdown
Member

@uumaro we're so sorry about this, in v5 we'll try to make it possible to filter by allocation type

JoTurk added a commit that referenced this pull request Sep 28, 2026
Change WithIPFilter to accept IPFilterInfo containing
local IP and candidate type.

context for this change:

#899 (comment)
@JoTurk

JoTurk commented Sep 28, 2026

Copy link
Copy Markdown
Member

@uumaro thank you again for reporting this, we're currently working on releasing v5 with some changes, and it's a good time to report this because we can change the API, if #1017 got merged, you can bring back the IP filter and it will look like:

ice.WithIPFilter(func(info ice.IPFilterInfo) bool {
    if info.CandidateType != ice.CandidateTypeHost {
        return true // allow local addresses for STUN/TURN.
    }

    return HostFilter(info.IP)
})

JoTurk added a commit that referenced this pull request Sep 30, 2026
Change WithIPFilter to accept IPFilterInfo containing
local IP and candidate type.

context for this change:

#899 (comment)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

STUN Requests are not filtered

4 participants