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 }