wf: allow limited broadcast to/from permitted interfaces when using an exit node on Windows

Similarly to allowing link-local multicast in #13661, we should also allow broadcast traffic
on permitted interfaces when the killswitch is enabled due to exit node usage on Windows.
This always includes internal interfaces, such as Hyper-V/WSL2, and also the LAN when
"Allow local network access" is enabled in the client.

Updates #18504

Signed-off-by: Nick Khyl <nickk@tailscale.com>
pull/14700/merge
Nick Khyl 2 days ago committed by Nick Khyl
parent 3ec5be3f51
commit 2a69f48541

@ -18,3 +18,6 @@ reference to an issue or PR about the feature.
When the option is disabled, we should still permit it for internal interfaces,
such as Hyper-V/WSL2 on Windows.
- Inbound and outbound broadcasts when an exit node is used, both with and without
the "Allow local network access" option enabled. When the option is disabled,
we should still permit traffic on internal interfaces, such as Hyper-V/WSL2 on Windows.

@ -25,6 +25,8 @@ var (
linkLocalMulticastIPv4Range = netip.MustParsePrefix("224.0.0.0/24")
linkLocalMulticastIPv6Range = netip.MustParsePrefix("ff02::/16")
limitedBroadcast = netip.MustParsePrefix("255.255.255.255/32")
)
type direction int
@ -233,26 +235,41 @@ func (f *Firewall) UpdatePermittedRoutes(newRoutes []netip.Prefix) error {
return err
}
name = "link-local multicast - " + r.String()
conditions = matchLinkLocalMulticast(r, false)
multicastRules, err := f.addRules(name, weightKnownTraffic, conditions, wf.ActionPermit, p, directionOutbound)
multicastRules, err := f.addLinkLocalMulticastRules(p, r)
if err != nil {
return err
}
rules = append(rules, multicastRules...)
conditions = matchLinkLocalMulticast(r, true)
multicastRules, err = f.addRules(name, weightKnownTraffic, conditions, wf.ActionPermit, p, directionInbound)
broadcastRules, err := f.addLimitedBroadcastRules(p, r)
if err != nil {
return err
}
rules = append(rules, multicastRules...)
rules = append(rules, broadcastRules...)
f.permittedRoutes[r] = rules
}
return nil
}
// addLinkLocalMulticastRules adds rules to allow inbound and outbound
// link-local multicast traffic to or from the specified network.
// It returns the added rules, or an error.
func (f *Firewall) addLinkLocalMulticastRules(p protocol, r netip.Prefix) ([]*wf.Rule, error) {
name := "link-local multicast - " + r.String()
conditions := matchLinkLocalMulticast(r, false)
outboundRules, err := f.addRules(name, weightKnownTraffic, conditions, wf.ActionPermit, p, directionOutbound)
if err != nil {
return nil, err
}
conditions = matchLinkLocalMulticast(r, true)
inboundRules, err := f.addRules(name, weightKnownTraffic, conditions, wf.ActionPermit, p, directionInbound)
if err != nil {
return nil, err
}
return append(outboundRules, inboundRules...), nil
}
// matchLinkLocalMulticast returns a list of conditions that match
// outbound or inbound link-local multicast traffic to or from the
// specified network.
@ -288,6 +305,59 @@ func matchLinkLocalMulticast(pfx netip.Prefix, inbound bool) []*wf.Match {
}
}
// addLimitedBroadcastRules adds rules to allow inbound and outbound
// limited broadcast traffic to or from the specified network,
// if the network is IPv4. It returns the added rules, or an error.
func (f *Firewall) addLimitedBroadcastRules(p protocol, r netip.Prefix) ([]*wf.Rule, error) {
if !r.Addr().Is4() {
return nil, nil
}
name := "broadcast - " + r.String()
conditions := matchLimitedBroadcast(r, false)
outboundRules, err := f.addRules(name, weightKnownTraffic, conditions, wf.ActionPermit, p, directionOutbound)
if err != nil {
return nil, err
}
conditions = matchLimitedBroadcast(r, true)
inboundRules, err := f.addRules(name, weightKnownTraffic, conditions, wf.ActionPermit, p, directionInbound)
if err != nil {
return nil, err
}
return append(outboundRules, inboundRules...), nil
}
// matchLimitedBroadcast returns a list of conditions that match
// outbound or inbound limited broadcast traffic to or from the
// specified network. It panics if the pfx is not IPv4.
func matchLimitedBroadcast(pfx netip.Prefix, inbound bool) []*wf.Match {
if !pfx.Addr().Is4() {
panic("limited broadcast is only applicable to IPv4")
}
var localAddr, remoteAddr netip.Prefix
if inbound {
localAddr, remoteAddr = limitedBroadcast, pfx
} else {
localAddr, remoteAddr = pfx, limitedBroadcast
}
return []*wf.Match{
{
Field: wf.FieldIPProtocol,
Op: wf.MatchTypeEqual,
Value: wf.IPProtoUDP,
},
{
Field: wf.FieldIPLocalAddress,
Op: wf.MatchTypeEqual,
Value: localAddr,
},
{
Field: wf.FieldIPRemoteAddress,
Op: wf.MatchTypeEqual,
Value: remoteAddr,
},
}
}
func (f *Firewall) newRule(name string, w weight, layer wf.LayerID, conditions []*wf.Match, action wf.Action) (*wf.Rule, error) {
id, err := windows.GenerateGUID()
if err != nil {

Loading…
Cancel
Save