github.com/gopacket/gopacket@v1.1.0/pcapgo/write.go (about)

     1  // Copyright 2012 Google, Inc. All rights reserved.
     2  //
     3  // Use of this source code is governed by a BSD-style license
     4  // that can be found in the LICENSE file in the root of the source
     5  // tree.
     6  
     7  package pcapgo
     8  
     9  import (
    10  	"encoding/binary"
    11  	"fmt"
    12  	"io"
    13  	"time"
    14  
    15  	"github.com/gopacket/gopacket"
    16  	"github.com/gopacket/gopacket/layers"
    17  )
    18  
    19  // Writer wraps an underlying io.Writer to write packet data in PCAP
    20  // format.  See http://wiki.wireshark.org/Development/LibpcapFileFormat
    21  // for information on the file format.
    22  //
    23  // For those that care, we currently write v2.4 files with nanosecond
    24  // or microsecond timestamp resolution and little-endian encoding.
    25  type Writer struct {
    26  	w        io.Writer
    27  	tsScaler int
    28  	// Moving this into the struct seems to save an allocation for each call to writePacketHeader
    29  	buf [16]byte
    30  }
    31  
    32  const magicMicroseconds = 0xA1B2C3D4
    33  const versionMajor = 2
    34  const versionMinor = 4
    35  
    36  // NewWriterNanos returns a new writer object, for writing packet data out
    37  // to the given writer.  If this is a new empty writer (as opposed to
    38  // an append), you must call WriteFileHeader before WritePacket.  Packet
    39  // timestamps are written with nanosecond precision.
    40  //
    41  //	// Write a new file:
    42  //	f, _ := os.Create("/tmp/file.pcap")
    43  //	w := pcapgo.NewWriterNanos(f)
    44  //	w.WriteFileHeader(65536, layers.LinkTypeEthernet)  // new file, must do this.
    45  //	w.WritePacket(gopacket.CaptureInfo{...}, data1)
    46  //	f.Close()
    47  //	// Append to existing file (must have same snaplen and linktype)
    48  //	f2, _ := os.OpenFile("/tmp/fileNano.pcap", os.O_APPEND, 0700)
    49  //	w2 := pcapgo.NewWriter(f2)
    50  //	// no need for file header, it's already written.
    51  //	w2.WritePacket(gopacket.CaptureInfo{...}, data2)
    52  //	f2.Close()
    53  func NewWriterNanos(w io.Writer) *Writer {
    54  	return &Writer{w: w, tsScaler: nanosPerNano}
    55  }
    56  
    57  // NewWriter returns a new writer object, for writing packet data out
    58  // to the given writer.  If this is a new empty writer (as opposed to
    59  // an append), you must call WriteFileHeader before WritePacket.
    60  // Packet timestamps are written witn microsecond precision.
    61  //
    62  //	// Write a new file:
    63  //	f, _ := os.Create("/tmp/file.pcap")
    64  //	w := pcapgo.NewWriter(f)
    65  //	w.WriteFileHeader(65536, layers.LinkTypeEthernet)  // new file, must do this.
    66  //	w.WritePacket(gopacket.CaptureInfo{...}, data1)
    67  //	f.Close()
    68  //	// Append to existing file (must have same snaplen and linktype)
    69  //	f2, _ := os.OpenFile("/tmp/file.pcap", os.O_APPEND, 0700)
    70  //	w2 := pcapgo.NewWriter(f2)
    71  //	// no need for file header, it's already written.
    72  //	w2.WritePacket(gopacket.CaptureInfo{...}, data2)
    73  //	f2.Close()
    74  func NewWriter(w io.Writer) *Writer {
    75  	return &Writer{w: w, tsScaler: nanosPerMicro}
    76  }
    77  
    78  // WriteFileHeader writes a file header out to the writer.
    79  // This must be called exactly once per output.
    80  func (w *Writer) WriteFileHeader(snaplen uint32, linktype layers.LinkType) error {
    81  	var buf [24]byte
    82  	if w.tsScaler == nanosPerMicro {
    83  		binary.LittleEndian.PutUint32(buf[0:4], magicMicroseconds)
    84  	} else {
    85  		binary.LittleEndian.PutUint32(buf[0:4], magicNanoseconds)
    86  	}
    87  	binary.LittleEndian.PutUint16(buf[4:6], versionMajor)
    88  	binary.LittleEndian.PutUint16(buf[6:8], versionMinor)
    89  	// bytes 8:12 stay 0 (timezone = UTC)
    90  	// bytes 12:16 stay 0 (sigfigs is always set to zero, according to
    91  	//   http://wiki.wireshark.org/Development/LibpcapFileFormat
    92  	binary.LittleEndian.PutUint32(buf[16:20], snaplen)
    93  	binary.LittleEndian.PutUint32(buf[20:24], uint32(linktype))
    94  	_, err := w.w.Write(buf[:])
    95  	return err
    96  }
    97  
    98  const nanosPerMicro = 1000
    99  const nanosPerNano = 1
   100  
   101  func (w *Writer) writePacketHeader(ci gopacket.CaptureInfo) error {
   102  	t := ci.Timestamp
   103  	if t.IsZero() {
   104  		t = time.Now()
   105  	}
   106  	secs := t.Unix()
   107  	usecs := t.Nanosecond() / w.tsScaler
   108  	binary.LittleEndian.PutUint32(w.buf[0:4], uint32(secs))
   109  	binary.LittleEndian.PutUint32(w.buf[4:8], uint32(usecs))
   110  	binary.LittleEndian.PutUint32(w.buf[8:12], uint32(ci.CaptureLength))
   111  	binary.LittleEndian.PutUint32(w.buf[12:16], uint32(ci.Length))
   112  	_, err := w.w.Write(w.buf[:])
   113  	return err
   114  }
   115  
   116  // WritePacket writes the given packet data out to the file.
   117  func (w *Writer) WritePacket(ci gopacket.CaptureInfo, data []byte) error {
   118  	if ci.CaptureLength != len(data) {
   119  		return fmt.Errorf("capture length %d does not match data length %d", ci.CaptureLength, len(data))
   120  	}
   121  	if ci.CaptureLength > ci.Length {
   122  		return fmt.Errorf("invalid capture info %+v:  capture length > length", ci)
   123  	}
   124  	if err := w.writePacketHeader(ci); err != nil {
   125  		return fmt.Errorf("error writing packet header: %v", err)
   126  	}
   127  	_, err := w.w.Write(data)
   128  	return err
   129  }