mirror of
https://github.com/tailscale/tailscale.git
synced 2024-11-29 04:55:31 +00:00
tstest/natlab/vnet: add pcap support
Updates #13038 Change-Id: I89ce2129fee856f97986d6313d2b661c76476c0c Signed-off-by: Maisem Ali <maisem@tailscale.com>
This commit is contained in:
parent
d0e8375b53
commit
d4cc074187
@ -33,7 +33,10 @@
|
|||||||
"tailscale.com/tstest/natlab/vnet"
|
"tailscale.com/tstest/natlab/vnet"
|
||||||
)
|
)
|
||||||
|
|
||||||
var logTailscaled = flag.Bool("log-tailscaled", false, "log tailscaled output")
|
var (
|
||||||
|
logTailscaled = flag.Bool("log-tailscaled", false, "log tailscaled output")
|
||||||
|
pcapFile = flag.String("pcap", "", "write pcap to file")
|
||||||
|
)
|
||||||
|
|
||||||
type natTest struct {
|
type natTest struct {
|
||||||
tb testing.TB
|
tb testing.TB
|
||||||
@ -142,6 +145,7 @@ func (nt *natTest) runTest(node1, node2 addNodeFunc) pingRoute {
|
|||||||
t := nt.tb
|
t := nt.tb
|
||||||
|
|
||||||
var c vnet.Config
|
var c vnet.Config
|
||||||
|
c.SetPCAPFile(*pcapFile)
|
||||||
nodes := []*vnet.Node{
|
nodes := []*vnet.Node{
|
||||||
node1(&c),
|
node1(&c),
|
||||||
node2(&c),
|
node2(&c),
|
||||||
|
@ -8,8 +8,11 @@
|
|||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"net/netip"
|
"net/netip"
|
||||||
|
"os"
|
||||||
"slices"
|
"slices"
|
||||||
|
|
||||||
|
"github.com/google/gopacket/layers"
|
||||||
|
"github.com/google/gopacket/pcapgo"
|
||||||
"tailscale.com/types/logger"
|
"tailscale.com/types/logger"
|
||||||
"tailscale.com/util/set"
|
"tailscale.com/util/set"
|
||||||
)
|
)
|
||||||
@ -27,6 +30,11 @@
|
|||||||
type Config struct {
|
type Config struct {
|
||||||
nodes []*Node
|
nodes []*Node
|
||||||
networks []*Network
|
networks []*Network
|
||||||
|
pcapFile string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Config) SetPCAPFile(file string) {
|
||||||
|
c.pcapFile = file
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Config) NumNodes() int {
|
func (c *Config) NumNodes() int {
|
||||||
@ -183,6 +191,21 @@ func (n *Network) AddService(s NetworkService) {
|
|||||||
// there were any configuration issues.
|
// there were any configuration issues.
|
||||||
func (s *Server) initFromConfig(c *Config) error {
|
func (s *Server) initFromConfig(c *Config) error {
|
||||||
netOfConf := map[*Network]*network{}
|
netOfConf := map[*Network]*network{}
|
||||||
|
if c.pcapFile != "" {
|
||||||
|
pcf, err := os.OpenFile(c.pcapFile, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0644)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
nw, err := pcapgo.NewNgWriter(pcf, layers.LinkTypeEthernet)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
pw := &pcapWriter{
|
||||||
|
f: pcf,
|
||||||
|
w: nw,
|
||||||
|
}
|
||||||
|
s.pcapWriter = pw
|
||||||
|
}
|
||||||
for _, conf := range c.networks {
|
for _, conf := range c.networks {
|
||||||
if conf.err != nil {
|
if conf.err != nil {
|
||||||
return conf.err
|
return conf.err
|
||||||
@ -206,14 +229,21 @@ func (s *Server) initFromConfig(c *Config) error {
|
|||||||
}
|
}
|
||||||
s.networkByWAN[conf.wanIP] = n
|
s.networkByWAN[conf.wanIP] = n
|
||||||
}
|
}
|
||||||
for _, conf := range c.nodes {
|
for i, conf := range c.nodes {
|
||||||
if conf.err != nil {
|
if conf.err != nil {
|
||||||
return conf.err
|
return conf.err
|
||||||
}
|
}
|
||||||
n := &node{
|
n := &node{
|
||||||
mac: conf.mac,
|
mac: conf.mac,
|
||||||
|
id: i + 1,
|
||||||
net: netOfConf[conf.Network()],
|
net: netOfConf[conf.Network()],
|
||||||
}
|
}
|
||||||
|
if s.pcapWriter != nil {
|
||||||
|
s.pcapWriter.w.AddInterface(pcapgo.NgInterface{
|
||||||
|
Name: fmt.Sprintf("node%d", n.id),
|
||||||
|
LinkType: layers.LinkTypeEthernet,
|
||||||
|
})
|
||||||
|
}
|
||||||
conf.n = n
|
conf.n = n
|
||||||
if _, ok := s.nodeByMAC[n.mac]; ok {
|
if _, ok := s.nodeByMAC[n.mac]; ok {
|
||||||
return fmt.Errorf("two nodes have the same MAC %v", n.mac)
|
return fmt.Errorf("two nodes have the same MAC %v", n.mac)
|
||||||
|
39
tstest/natlab/vnet/pcap.go
Normal file
39
tstest/natlab/vnet/pcap.go
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
// Copyright (c) Tailscale Inc & AUTHORS
|
||||||
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
|
||||||
|
package vnet
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/google/gopacket"
|
||||||
|
"github.com/google/gopacket/pcapgo"
|
||||||
|
)
|
||||||
|
|
||||||
|
type pcapWriter struct {
|
||||||
|
f *os.File
|
||||||
|
|
||||||
|
mu sync.Mutex
|
||||||
|
w *pcapgo.NgWriter
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *pcapWriter) WritePacket(ci gopacket.CaptureInfo, data []byte) error {
|
||||||
|
p.mu.Lock()
|
||||||
|
defer p.mu.Unlock()
|
||||||
|
if p.w == nil {
|
||||||
|
return io.ErrClosedPipe
|
||||||
|
}
|
||||||
|
return p.w.WritePacket(ci, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *pcapWriter) Close() error {
|
||||||
|
p.mu.Lock()
|
||||||
|
defer p.mu.Unlock()
|
||||||
|
if p.w != nil {
|
||||||
|
p.w.Flush()
|
||||||
|
p.w = nil
|
||||||
|
}
|
||||||
|
return p.f.Close()
|
||||||
|
}
|
@ -31,6 +31,7 @@
|
|||||||
"os/exec"
|
"os/exec"
|
||||||
"strconv"
|
"strconv"
|
||||||
"sync"
|
"sync"
|
||||||
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/google/gopacket"
|
"github.com/google/gopacket"
|
||||||
@ -58,6 +59,7 @@
|
|||||||
"tailscale.com/types/key"
|
"tailscale.com/types/key"
|
||||||
"tailscale.com/types/logger"
|
"tailscale.com/types/logger"
|
||||||
"tailscale.com/util/mak"
|
"tailscale.com/util/mak"
|
||||||
|
"tailscale.com/util/must"
|
||||||
"tailscale.com/util/set"
|
"tailscale.com/util/set"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -444,6 +446,7 @@ func (n *network) MACOfIP(ip netip.Addr) (_ MAC, ok bool) {
|
|||||||
|
|
||||||
type node struct {
|
type node struct {
|
||||||
mac MAC
|
mac MAC
|
||||||
|
id int
|
||||||
net *network
|
net *network
|
||||||
lanIP netip.Addr // must be in net.lanIP prefix + unique in net
|
lanIP netip.Addr // must be in net.lanIP prefix + unique in net
|
||||||
}
|
}
|
||||||
@ -474,6 +477,8 @@ func newDERPServer() *derpServer {
|
|||||||
type Server struct {
|
type Server struct {
|
||||||
shutdownCtx context.Context
|
shutdownCtx context.Context
|
||||||
shutdownCancel context.CancelFunc
|
shutdownCancel context.CancelFunc
|
||||||
|
shuttingDown atomic.Bool
|
||||||
|
wg sync.WaitGroup
|
||||||
blendReality bool
|
blendReality bool
|
||||||
|
|
||||||
derpIPs set.Set[netip.Addr]
|
derpIPs set.Set[netip.Addr]
|
||||||
@ -483,8 +488,9 @@ type Server struct {
|
|||||||
networks set.Set[*network]
|
networks set.Set[*network]
|
||||||
networkByWAN map[netip.Addr]*network
|
networkByWAN map[netip.Addr]*network
|
||||||
|
|
||||||
control *testcontrol.Server
|
control *testcontrol.Server
|
||||||
derps []*derpServer
|
derps []*derpServer
|
||||||
|
pcapWriter *pcapWriter
|
||||||
|
|
||||||
mu sync.Mutex
|
mu sync.Mutex
|
||||||
agentConnWaiter map[*node]chan<- struct{} // signaled after added to set
|
agentConnWaiter map[*node]chan<- struct{} // signaled after added to set
|
||||||
@ -562,7 +568,13 @@ func New(c *Config) (*Server, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) Close() {
|
func (s *Server) Close() {
|
||||||
s.shutdownCancel()
|
if shutdown := s.shuttingDown.Swap(true); !shutdown {
|
||||||
|
s.shutdownCancel()
|
||||||
|
if s.pcapWriter != nil {
|
||||||
|
s.pcapWriter.Close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
s.wg.Wait()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) HWAddr(mac MAC) net.HardwareAddr {
|
func (s *Server) HWAddr(mac MAC) net.HardwareAddr {
|
||||||
@ -601,11 +613,20 @@ func (s *Server) IPv4ForDNS(qname string) (netip.Addr, bool) {
|
|||||||
|
|
||||||
// serveConn serves a single connection from a client.
|
// serveConn serves a single connection from a client.
|
||||||
func (s *Server) ServeUnixConn(uc *net.UnixConn, proto Protocol) {
|
func (s *Server) ServeUnixConn(uc *net.UnixConn, proto Protocol) {
|
||||||
|
if s.shuttingDown.Load() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
s.wg.Add(1)
|
||||||
|
defer s.wg.Done()
|
||||||
|
context.AfterFunc(s.shutdownCtx, func() {
|
||||||
|
uc.SetDeadline(time.Now())
|
||||||
|
})
|
||||||
log.Printf("Got conn %T %p", uc, uc)
|
log.Printf("Got conn %T %p", uc, uc)
|
||||||
defer uc.Close()
|
defer uc.Close()
|
||||||
|
|
||||||
bw := bufio.NewWriterSize(uc, 2<<10)
|
bw := bufio.NewWriterSize(uc, 2<<10)
|
||||||
var writeMu sync.Mutex
|
var writeMu sync.Mutex
|
||||||
|
var srcNode *node
|
||||||
writePkt := func(pkt []byte) {
|
writePkt := func(pkt []byte) {
|
||||||
if pkt == nil {
|
if pkt == nil {
|
||||||
return
|
return
|
||||||
@ -626,10 +647,20 @@ func (s *Server) ServeUnixConn(uc *net.UnixConn, proto Protocol) {
|
|||||||
if err := bw.Flush(); err != nil {
|
if err := bw.Flush(); err != nil {
|
||||||
log.Printf("Flush: %v", err)
|
log.Printf("Flush: %v", err)
|
||||||
}
|
}
|
||||||
|
if s.pcapWriter != nil {
|
||||||
|
ci := gopacket.CaptureInfo{
|
||||||
|
Timestamp: time.Now(),
|
||||||
|
CaptureLength: len(pkt),
|
||||||
|
Length: len(pkt),
|
||||||
|
}
|
||||||
|
if srcNode != nil {
|
||||||
|
ci.InterfaceIndex = srcNode.id
|
||||||
|
}
|
||||||
|
must.Do(s.pcapWriter.WritePacket(ci, pkt))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
buf := make([]byte, 16<<10)
|
buf := make([]byte, 16<<10)
|
||||||
var srcNode *node
|
|
||||||
var netw *network // non-nil after first packet
|
var netw *network // non-nil after first packet
|
||||||
for {
|
for {
|
||||||
var packetRaw []byte
|
var packetRaw []byte
|
||||||
@ -686,6 +717,17 @@ func (s *Server) ServeUnixConn(uc *net.UnixConn, proto Protocol) {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if s.pcapWriter != nil {
|
||||||
|
ci := gopacket.CaptureInfo{
|
||||||
|
Timestamp: time.Now(),
|
||||||
|
CaptureLength: len(packetRaw),
|
||||||
|
Length: len(packetRaw),
|
||||||
|
}
|
||||||
|
if srcNode != nil {
|
||||||
|
ci.InterfaceIndex = srcNode.id
|
||||||
|
}
|
||||||
|
must.Do(s.pcapWriter.WritePacket(ci, packetRaw))
|
||||||
|
}
|
||||||
netw.HandleEthernetPacket(ep)
|
netw.HandleEthernetPacket(ep)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -840,7 +882,6 @@ func (n *network) WriteUDPPacketNoNAT(p UDPPacket) {
|
|||||||
// IP may be the router's IP, or an internet (routed) IP.
|
// IP may be the router's IP, or an internet (routed) IP.
|
||||||
func (n *network) HandleEthernetIPv4PacketForRouter(ep EthernetPacket) {
|
func (n *network) HandleEthernetIPv4PacketForRouter(ep EthernetPacket) {
|
||||||
packet := ep.gp
|
packet := ep.gp
|
||||||
writePkt := n.writeEth
|
|
||||||
|
|
||||||
v4, ok := packet.Layer(layers.LayerTypeIPv4).(*layers.IPv4)
|
v4, ok := packet.Layer(layers.LayerTypeIPv4).(*layers.IPv4)
|
||||||
if !ok {
|
if !ok {
|
||||||
@ -857,7 +898,7 @@ func (n *network) HandleEthernetIPv4PacketForRouter(ep EthernetPacket) {
|
|||||||
n.logf("createDHCPResponse: %v", err)
|
n.logf("createDHCPResponse: %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
writePkt(res)
|
n.writeEth(res)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -874,7 +915,7 @@ func (n *network) HandleEthernetIPv4PacketForRouter(ep EthernetPacket) {
|
|||||||
n.logf("createDNSResponse: %v", err)
|
n.logf("createDNSResponse: %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
writePkt(res)
|
n.writeEth(res)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user