// Package pcap writes classic libpcap capture files. // // Format reference: https://www.ietf.org/archive/id/draft-ietf-opsawg-pcap-04.html package pcap import ( "encoding/binary" "fmt" "io" "time" ) // DefaultSnapLen is the snapshot length written to the file header. const DefaultSnapLen = 262144 const magicMicroseconds = 0xa1b2c3d4 // Common link-layer header types (LINKTYPE_* values from tcpdump.org). const ( LinkTypeEthernet uint32 = 1 LinkTypeRaw uint32 = 101 LinkTypeIEEE80211 uint32 = 105 LinkTypeLinuxSLL uint32 = 113 LinkTypeLinuxSLL2 uint32 = 276 ) // LinkTypeName returns a human readable name for well known link types. func LinkTypeName(lt uint32) string { switch lt { case LinkTypeEthernet: return "Ethernet" case LinkTypeRaw: return "Raw IP" case LinkTypeIEEE80211: return "IEEE 802.11" case LinkTypeLinuxSLL: return "Linux cooked (SLL)" case LinkTypeLinuxSLL2: return "Linux cooked v2 (SLL2)" } return fmt.Sprintf("LINKTYPE %d", lt) } // Writer writes packets to a pcap file. The file header is written by NewWriter. type Writer struct { w io.Writer snapLen uint32 } // NewWriter writes a pcap file header (microsecond resolution, little endian) // for the given link type and returns a Writer for appending packets. func NewWriter(w io.Writer, linkType uint32) (*Writer, error) { var hdr [24]byte binary.LittleEndian.PutUint32(hdr[0:], magicMicroseconds) binary.LittleEndian.PutUint16(hdr[4:], 2) // version major binary.LittleEndian.PutUint16(hdr[6:], 4) // version minor // hdr[8:16]: thiszone and sigfigs, always zero binary.LittleEndian.PutUint32(hdr[16:], DefaultSnapLen) binary.LittleEndian.PutUint32(hdr[20:], linkType) if _, err := w.Write(hdr[:]); err != nil { return nil, err } return &Writer{w: w, snapLen: DefaultSnapLen}, nil } // WritePacket appends one packet record with the given timestamp. func (w *Writer) WritePacket(ts time.Time, data []byte) error { if len(data) > int(w.snapLen) { return fmt.Errorf("packet length %d exceeds snaplen %d", len(data), w.snapLen) } var hdr [16]byte binary.LittleEndian.PutUint32(hdr[0:], uint32(ts.Unix())) binary.LittleEndian.PutUint32(hdr[4:], uint32(ts.Nanosecond()/1000)) binary.LittleEndian.PutUint32(hdr[8:], uint32(len(data))) // captured length binary.LittleEndian.PutUint32(hdr[12:], uint32(len(data))) // original length if _, err := w.w.Write(hdr[:]); err != nil { return err } _, err := w.w.Write(data) return err }