tstest/natlab: add test for no control and rotated disco key (#18261)
Updates #12639 Signed-off-by: Claus Lensbøl <claus@tailscale.com>
This commit is contained in:
@@ -5,6 +5,7 @@ package vnet
|
||||
|
||||
import (
|
||||
"cmp"
|
||||
"context"
|
||||
"fmt"
|
||||
"iter"
|
||||
"net/netip"
|
||||
@@ -114,6 +115,10 @@ func (c *Config) AddNode(opts ...any) *Node {
|
||||
switch o {
|
||||
case HostFirewall:
|
||||
n.hostFW = true
|
||||
case RotateDisco:
|
||||
n.rotateDisco = true
|
||||
case PreICMPPing:
|
||||
n.preICMPPing = true
|
||||
case VerboseSyslog:
|
||||
n.verboseSyslog = true
|
||||
default:
|
||||
@@ -137,6 +142,8 @@ type NodeOption string
|
||||
|
||||
const (
|
||||
HostFirewall NodeOption = "HostFirewall"
|
||||
RotateDisco NodeOption = "RotateDisco"
|
||||
PreICMPPing NodeOption = "PreICMPPing"
|
||||
VerboseSyslog NodeOption = "VerboseSyslog"
|
||||
)
|
||||
|
||||
@@ -197,12 +204,15 @@ func (c *Config) AddNetwork(opts ...any) *Network {
|
||||
|
||||
// Node is the configuration of a node in the virtual network.
|
||||
type Node struct {
|
||||
err error
|
||||
num int // 1-based node number
|
||||
n *node // nil until NewServer called
|
||||
err error
|
||||
num int // 1-based node number
|
||||
n *node // nil until NewServer called
|
||||
client *NodeAgentClient
|
||||
|
||||
env []TailscaledEnv
|
||||
hostFW bool
|
||||
rotateDisco bool
|
||||
preICMPPing bool
|
||||
verboseSyslog bool
|
||||
|
||||
// TODO(bradfitz): this is halfway converted to supporting multiple NICs
|
||||
@@ -243,6 +253,33 @@ func (n *Node) SetVerboseSyslog(v bool) {
|
||||
n.verboseSyslog = v
|
||||
}
|
||||
|
||||
func (n *Node) SetClient(c *NodeAgentClient) {
|
||||
n.client = c
|
||||
}
|
||||
|
||||
// PostConnectedToControl should be called after the clients have connected to
|
||||
// control to modify the client behaviour after getting the network maps.
|
||||
// Currently, the only implemented behavior is rotating disco keys.
|
||||
func (n *Node) PostConnectedToControl(ctx context.Context) error {
|
||||
if n.rotateDisco {
|
||||
if err := n.client.DebugAction(ctx, "rotate-disco-key"); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// PreICMPPing reports whether node should send an ICMP Ping sent before
|
||||
// the disco ping. This is important for the nodes having rotated their
|
||||
// disco keys while control is down. Disco pings deliberately does not
|
||||
// trigger a TSMPDiscoKeyAdvertisement, making the need for other traffic (here
|
||||
// simlulated as an ICMP ping) needed first. Any traffic could trigger this key
|
||||
// exchange, the ICMP Ping is used as a handy existing way of sending some
|
||||
// non-disco traffic.
|
||||
func (n *Node) PreICMPPing() bool {
|
||||
return n.preICMPPing
|
||||
}
|
||||
|
||||
// IsV6Only reports whether this node is only connected to IPv6 networks.
|
||||
func (n *Node) IsV6Only() bool {
|
||||
for _, net := range n.nets {
|
||||
@@ -275,10 +312,12 @@ type Network struct {
|
||||
|
||||
wanIP6 netip.Prefix // global unicast router in host bits; CIDR is /64 delegated to LAN
|
||||
|
||||
wanIP4 netip.Addr // IPv4 WAN IP, if any
|
||||
lanIP4 netip.Prefix
|
||||
nodes []*Node
|
||||
breakWAN4 bool // whether to break WAN IPv4 connectivity
|
||||
wanIP4 netip.Addr // IPv4 WAN IP, if any
|
||||
lanIP4 netip.Prefix
|
||||
nodes []*Node
|
||||
breakWAN4 bool // whether to break WAN IPv4 connectivity
|
||||
postConnectBlackholeControl bool // whether to break control connectivity after nodes have connected
|
||||
network *network
|
||||
|
||||
svcs set.Set[NetworkService]
|
||||
|
||||
@@ -310,6 +349,12 @@ func (n *Network) SetBlackholedIPv4(v bool) {
|
||||
n.breakWAN4 = v
|
||||
}
|
||||
|
||||
// SetPostConnectControlBlackhole sets wether the network should blackhole all
|
||||
// traffic to the control server after the clients have connected.
|
||||
func (n *Network) SetPostConnectControlBlackhole(v bool) {
|
||||
n.postConnectBlackholeControl = v
|
||||
}
|
||||
|
||||
func (n *Network) CanV4() bool {
|
||||
return n.lanIP4.IsValid() || n.wanIP4.IsValid()
|
||||
}
|
||||
@@ -325,6 +370,13 @@ func (n *Network) CanTakeMoreNodes() bool {
|
||||
return len(n.nodes) < 150
|
||||
}
|
||||
|
||||
// PostConnectedToControl should be called after the clients have connected to
|
||||
// the control server to modify network behaviors. Currently the only
|
||||
// implemented behavior is to conditionally blackhole traffic to control.
|
||||
func (n *Network) PostConnectedToControl() {
|
||||
n.network.SetControlBlackholed(n.postConnectBlackholeControl)
|
||||
}
|
||||
|
||||
// NetworkService is a service that can be added to a network.
|
||||
type NetworkService string
|
||||
|
||||
@@ -390,6 +442,8 @@ func (s *Server) initFromConfig(c *Config) error {
|
||||
}
|
||||
netOfConf[conf] = n
|
||||
s.networks.Add(n)
|
||||
|
||||
conf.network = n
|
||||
if conf.wanIP4.IsValid() {
|
||||
if conf.wanIP4.Is6() {
|
||||
return fmt.Errorf("invalid IPv6 address in wanIP")
|
||||
|
||||
+26
-18
@@ -506,23 +506,24 @@ func (nw networkWriter) write(b []byte) {
|
||||
}
|
||||
|
||||
type network struct {
|
||||
s *Server
|
||||
num int // 1-based
|
||||
mac MAC // of router
|
||||
portmap bool
|
||||
lanInterfaceID int
|
||||
wanInterfaceID int
|
||||
v4 bool // network supports IPv4
|
||||
v6 bool // network support IPv6
|
||||
wanIP6 netip.Prefix // router's WAN IPv6, if any, as a /64.
|
||||
wanIP4 netip.Addr // router's LAN IPv4, if any
|
||||
lanIP4 netip.Prefix // router's LAN IP + CIDR (e.g. 192.168.2.1/24)
|
||||
breakWAN4 bool // break WAN IPv4 connectivity
|
||||
latency time.Duration // latency applied to interface writes
|
||||
lossRate float64 // probability of dropping a packet (0.0 to 1.0)
|
||||
nodesByIP4 map[netip.Addr]*node // by LAN IPv4
|
||||
nodesByMAC map[MAC]*node
|
||||
logf func(format string, args ...any)
|
||||
s *Server
|
||||
num int // 1-based
|
||||
mac MAC // of router
|
||||
portmap bool
|
||||
lanInterfaceID int
|
||||
wanInterfaceID int
|
||||
v4 bool // network supports IPv4
|
||||
v6 bool // network support IPv6
|
||||
wanIP6 netip.Prefix // router's WAN IPv6, if any, as a /64.
|
||||
wanIP4 netip.Addr // router's LAN IPv4, if any
|
||||
lanIP4 netip.Prefix // router's LAN IP + CIDR (e.g. 192.168.2.1/24)
|
||||
breakWAN4 bool // break WAN IPv4 connectivity
|
||||
blackholeControl bool // blackhole control connectivity
|
||||
latency time.Duration // latency applied to interface writes
|
||||
lossRate float64 // probability of dropping a packet (0.0 to 1.0)
|
||||
nodesByIP4 map[netip.Addr]*node // by LAN IPv4
|
||||
nodesByMAC map[MAC]*node
|
||||
logf func(format string, args ...any)
|
||||
|
||||
ns *stack.Stack
|
||||
linkEP *channel.Endpoint
|
||||
@@ -578,6 +579,12 @@ func (n *network) MACOfIP(ip netip.Addr) (_ MAC, ok bool) {
|
||||
return MAC{}, false
|
||||
}
|
||||
|
||||
// SetControlBlackholed sets wether traffic to control should be blackholed for the
|
||||
// network.
|
||||
func (n *network) SetControlBlackholed(v bool) {
|
||||
n.blackholeControl = v
|
||||
}
|
||||
|
||||
type node struct {
|
||||
mac MAC
|
||||
num int // 1-based node number
|
||||
@@ -1263,7 +1270,8 @@ func (n *network) HandleEthernetPacketForRouter(ep EthernetPacket) {
|
||||
}
|
||||
|
||||
if toForward && n.s.shouldInterceptTCP(packet) {
|
||||
if flow.dst.Is4() && n.breakWAN4 {
|
||||
if (flow.dst.Is4() && n.breakWAN4) ||
|
||||
(n.blackholeControl && fakeControl.Match(flow.dst)) {
|
||||
// Blackhole the packet.
|
||||
return
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user