util/linuxfw,wgengine/router: allow incoming CGNAT range traffic with nodeattr
Clients with the newly added node attribute `"disable-linux-cgnat-drop-rule"` will not automatically drop inbound traffic on non-Tailscale network interfaces with the source IP in the CGNAT IP range. This is an initial proof-of-concept for enabling connectivity with off-Tailnet CGNAT endpoints. Fixes tailscale/corp#36270. Signed-off-by: Naman Sood <mail@nsood.in>
This commit is contained in:
@@ -593,6 +593,15 @@ type NetfilterRunner interface {
|
||||
// DelMagicsockPortRule removes the rule created by AddMagicsockPortRule,
|
||||
// if it exists.
|
||||
DelMagicsockPortRule(port uint16, network string) error
|
||||
|
||||
// AddExternalCGNATRules adds rules to the ts-input chain to deal with
|
||||
// traffic from the CGNAT range that arrives on non-Tailscale network
|
||||
// interfaces.
|
||||
AddExternalCGNATRules(mode CGNATMode, tunname string) error
|
||||
|
||||
// DelExternalCGNATRules removes the rules created by AddExternalCGNATRules,
|
||||
// if they exist.
|
||||
DelExternalCGNATRules(mode CGNATMode, tunname string) error
|
||||
}
|
||||
|
||||
// New creates a NetfilterRunner, auto-detecting whether to use
|
||||
@@ -1221,6 +1230,27 @@ func addReturnChromeOSVMRangeRule(c *nftables.Conn, table *nftables.Table, chain
|
||||
return nil
|
||||
}
|
||||
|
||||
// delReturnChromeOSVMRangeRule deletes the rule created by addReturnChromeOSVMRangeRule,
|
||||
// if it exists.
|
||||
func delReturnChromeOSVMRangeRule(c *nftables.Conn, table *nftables.Table, chain *nftables.Chain, tunname string) error {
|
||||
rule, err := createRangeRule(table, chain, tunname, tsaddr.ChromeOSVMRange(), expr.VerdictReturn)
|
||||
if err != nil {
|
||||
return fmt.Errorf("create rule: %w", err)
|
||||
}
|
||||
rule, err = findRule(c, rule)
|
||||
if err != nil {
|
||||
return fmt.Errorf("find rule: %v", err)
|
||||
}
|
||||
if rule == nil {
|
||||
return nil
|
||||
}
|
||||
_ = c.DelRule(rule)
|
||||
if err := c.Flush(); err != nil {
|
||||
return fmt.Errorf("flush del rule: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// addDropCGNATRangeRule adds a rule to drop if the source IP is in the
|
||||
// CGNAT range.
|
||||
func addDropCGNATRangeRule(c *nftables.Conn, table *nftables.Table, chain *nftables.Chain, tunname string) error {
|
||||
@@ -1235,6 +1265,62 @@ func addDropCGNATRangeRule(c *nftables.Conn, table *nftables.Table, chain *nftab
|
||||
return nil
|
||||
}
|
||||
|
||||
// delDropCGNATRangeRule deletes the rule created by addDropCGNATRangeRule,
|
||||
// if it exists.
|
||||
func delDropCGNATRangeRule(c *nftables.Conn, table *nftables.Table, chain *nftables.Chain, tunname string) error {
|
||||
rule, err := createRangeRule(table, chain, tunname, tsaddr.CGNATRange(), expr.VerdictDrop)
|
||||
if err != nil {
|
||||
return fmt.Errorf("create rule: %w", err)
|
||||
}
|
||||
rule, err = findRule(c, rule)
|
||||
if err != nil {
|
||||
return fmt.Errorf("find rule: %v", err)
|
||||
}
|
||||
if rule == nil {
|
||||
return nil
|
||||
}
|
||||
_ = c.DelRule(rule)
|
||||
if err := c.Flush(); err != nil {
|
||||
return fmt.Errorf("flush del rule: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// addReturnCGNATRangeRule adds a rule to return if the source IP is in the
|
||||
// CGNAT range.
|
||||
func addReturnCGNATRangeRule(c *nftables.Conn, table *nftables.Table, chain *nftables.Chain, tunname string) error {
|
||||
rule, err := createRangeRule(table, chain, tunname, tsaddr.CGNATRange(), expr.VerdictReturn)
|
||||
if err != nil {
|
||||
return fmt.Errorf("create rule: %w", err)
|
||||
}
|
||||
_ = c.AddRule(rule)
|
||||
if err = c.Flush(); err != nil {
|
||||
return fmt.Errorf("add rule: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// delReturnCGNATRangeRule deletes the rule created by addReturnCGNATRangeRule,
|
||||
// if it exists.
|
||||
func delReturnCGNATRangeRule(c *nftables.Conn, table *nftables.Table, chain *nftables.Chain, tunname string) error {
|
||||
rule, err := createRangeRule(table, chain, tunname, tsaddr.CGNATRange(), expr.VerdictReturn)
|
||||
if err != nil {
|
||||
return fmt.Errorf("create rule: %w", err)
|
||||
}
|
||||
rule, err = findRule(c, rule)
|
||||
if err != nil {
|
||||
return fmt.Errorf("find rule: %v", err)
|
||||
}
|
||||
if rule == nil {
|
||||
return nil
|
||||
}
|
||||
_ = c.DelRule(rule)
|
||||
if err := c.Flush(); err != nil {
|
||||
return fmt.Errorf("flush del rule: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// createSetSubnetRouteMarkRule creates a rule to set the subnet route
|
||||
// mark if the packet is from the given interface.
|
||||
func createSetSubnetRouteMarkRule(table *nftables.Table, chain *nftables.Chain, tunname string) (*nftables.Rule, error) {
|
||||
@@ -1502,6 +1588,67 @@ func (n *nftablesRunner) DelMagicsockPortRule(port uint16, network string) error
|
||||
return nil
|
||||
}
|
||||
|
||||
// AddExternalCGNATRules adds rules to the ts-input chain to deal with
|
||||
// traffic from the CGNAT range that arrives on non-Tailscale network
|
||||
// interfaces.
|
||||
func (n *nftablesRunner) AddExternalCGNATRules(mode CGNATMode, tunname string) error {
|
||||
conn := n.conn
|
||||
|
||||
inputChain, err := getChainFromTable(conn, n.nft4.Filter, chainNameInput)
|
||||
if err != nil {
|
||||
return fmt.Errorf("get input chain v4: %v", err)
|
||||
}
|
||||
switch mode {
|
||||
case CGNATModeDrop:
|
||||
if err = addReturnChromeOSVMRangeRule(conn, n.nft4.Filter, inputChain, tunname); err != nil {
|
||||
return fmt.Errorf("add return chromeos vm range rule v4: %w", err)
|
||||
}
|
||||
if err = addDropCGNATRangeRule(conn, n.nft4.Filter, inputChain, tunname); err != nil {
|
||||
return fmt.Errorf("add drop cgnat range rule v4: %w", err)
|
||||
}
|
||||
case CGNATModeReturn:
|
||||
if err = addReturnCGNATRangeRule(conn, n.nft4.Filter, inputChain, tunname); err != nil {
|
||||
return fmt.Errorf("add return cgnat range rule v4: %w", err)
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf("unsupported cgnat mode %q", mode)
|
||||
}
|
||||
if err = conn.Flush(); err != nil {
|
||||
return fmt.Errorf("flush cgnat rules v4: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DelExternalCGNATRules removes the rules created by AddExternalCGNATRules,
|
||||
// if they exist.
|
||||
func (n *nftablesRunner) DelExternalCGNATRules(mode CGNATMode, tunname string) error {
|
||||
conn := n.conn
|
||||
|
||||
inputChain, err := getChainFromTable(conn, n.nft4.Filter, chainNameInput)
|
||||
if err != nil {
|
||||
return fmt.Errorf("get input chain v4: %v", err)
|
||||
}
|
||||
switch mode {
|
||||
case CGNATModeDrop:
|
||||
if err = delReturnChromeOSVMRangeRule(conn, n.nft4.Filter, inputChain, tunname); err != nil {
|
||||
return fmt.Errorf("del return chromeos vm range rule v4: %w", err)
|
||||
}
|
||||
if err = delDropCGNATRangeRule(conn, n.nft4.Filter, inputChain, tunname); err != nil {
|
||||
return fmt.Errorf("del drop cgnat range rule v4: %w", err)
|
||||
}
|
||||
case CGNATModeReturn:
|
||||
if err = delReturnCGNATRangeRule(conn, n.nft4.Filter, inputChain, tunname); err != nil {
|
||||
return fmt.Errorf("del return cgnat range rule v4: %w", err)
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf("unsupported mode %q", mode)
|
||||
}
|
||||
if err = conn.Flush(); err != nil {
|
||||
return fmt.Errorf("flush cgnat rules v4: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// createAcceptIncomingPacketRule creates a rule to accept incoming packets to
|
||||
// the given interface.
|
||||
func createAcceptIncomingPacketRule(table *nftables.Table, chain *nftables.Chain, tunname string) *nftables.Rule {
|
||||
@@ -1555,12 +1702,6 @@ func (n *nftablesRunner) addBase4(tunname string) error {
|
||||
if err != nil {
|
||||
return fmt.Errorf("get input chain v4: %v", err)
|
||||
}
|
||||
if err = addReturnChromeOSVMRangeRule(conn, n.nft4.Filter, inputChain, tunname); err != nil {
|
||||
return fmt.Errorf("add return chromeos vm range rule v4: %w", err)
|
||||
}
|
||||
if err = addDropCGNATRangeRule(conn, n.nft4.Filter, inputChain, tunname); err != nil {
|
||||
return fmt.Errorf("add drop cgnat range rule v4: %w", err)
|
||||
}
|
||||
if err = addAcceptIncomingPacketRule(conn, n.nft4.Filter, inputChain, tunname); err != nil {
|
||||
return fmt.Errorf("add accept incoming packet rule v4: %w", err)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user