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>
This commit is contained in:
@@ -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,
|
When the option is disabled, we should still permit it for internal interfaces,
|
||||||
such as Hyper-V/WSL2 on Windows.
|
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.
|
||||||
+76
-6
@@ -25,6 +25,8 @@ var (
|
|||||||
|
|
||||||
linkLocalMulticastIPv4Range = netip.MustParsePrefix("224.0.0.0/24")
|
linkLocalMulticastIPv4Range = netip.MustParsePrefix("224.0.0.0/24")
|
||||||
linkLocalMulticastIPv6Range = netip.MustParsePrefix("ff02::/16")
|
linkLocalMulticastIPv6Range = netip.MustParsePrefix("ff02::/16")
|
||||||
|
|
||||||
|
limitedBroadcast = netip.MustParsePrefix("255.255.255.255/32")
|
||||||
)
|
)
|
||||||
|
|
||||||
type direction int
|
type direction int
|
||||||
@@ -233,26 +235,41 @@ func (f *Firewall) UpdatePermittedRoutes(newRoutes []netip.Prefix) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
name = "link-local multicast - " + r.String()
|
multicastRules, err := f.addLinkLocalMulticastRules(p, r)
|
||||||
conditions = matchLinkLocalMulticast(r, false)
|
|
||||||
multicastRules, err := f.addRules(name, weightKnownTraffic, conditions, wf.ActionPermit, p, directionOutbound)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
rules = append(rules, multicastRules...)
|
rules = append(rules, multicastRules...)
|
||||||
|
|
||||||
conditions = matchLinkLocalMulticast(r, true)
|
broadcastRules, err := f.addLimitedBroadcastRules(p, r)
|
||||||
multicastRules, err = f.addRules(name, weightKnownTraffic, conditions, wf.ActionPermit, p, directionInbound)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
rules = append(rules, multicastRules...)
|
rules = append(rules, broadcastRules...)
|
||||||
|
|
||||||
f.permittedRoutes[r] = rules
|
f.permittedRoutes[r] = rules
|
||||||
}
|
}
|
||||||
return nil
|
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
|
// matchLinkLocalMulticast returns a list of conditions that match
|
||||||
// outbound or inbound link-local multicast traffic to or from the
|
// outbound or inbound link-local multicast traffic to or from the
|
||||||
// specified network.
|
// 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) {
|
func (f *Firewall) newRule(name string, w weight, layer wf.LayerID, conditions []*wf.Match, action wf.Action) (*wf.Rule, error) {
|
||||||
id, err := windows.GenerateGUID()
|
id, err := windows.GenerateGUID()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
Reference in New Issue
Block a user