// Copyright (c) Tailscale Inc & contributors // SPDX-License-Identifier: BSD-3-Clause package vnet import ( "io" "os" "sync" "github.com/google/gopacket" "github.com/google/gopacket/pcapgo" ) // pcapWriter is a pcapgo.NgWriter that writes to a file. // It is safe for concurrent use. The nil value is a no-op. type pcapWriter struct { f *os.File mu sync.Mutex w *pcapgo.NgWriter } func do(fs ...func() error) error { for _, f := range fs { if err := f(); err != nil { return err } } return nil } func (p *pcapWriter) WritePacket(ci gopacket.CaptureInfo, data []byte) error { if p == nil { return nil } p.mu.Lock() defer p.mu.Unlock() if p.w == nil { return io.ErrClosedPipe } // Flush per packet so the file is readable if the process dies // mid-test, but don't fsync: an fsync per packet serializes every // packet behind a disk write and caps vnet TCP throughput at a few // hundred kB/s (each data segment pays a couple of milliseconds // before the receiver can ACK it). return do( func() error { return p.w.WritePacket(ci, data) }, p.w.Flush, ) } func (p *pcapWriter) AddInterface(i pcapgo.NgInterface) (int, error) { if p == nil { return 0, nil } p.mu.Lock() defer p.mu.Unlock() return p.w.AddInterface(i) } func (p *pcapWriter) Close() error { if p == nil { return nil } p.mu.Lock() defer p.mu.Unlock() if p.w != nil { p.w.Flush() p.w = nil } return p.f.Close() }