mirror of
https://github.com/ossrs/srs.git
synced 2025-03-09 15:49:59 +00:00
HLS: Ignore empty NALU to avoid error. v6.0.65 (#3750)
For the DJI M30, there is a bug where empty NALU packets with a size of zero are causing issues with HLS streaming. This bug leads to random unpublish events due to the SRS disconnecting the connection for the HLS module when it fails to handle empty NALU packets. To address this bug, we have patched the system to ignore any empty NALU packets with a size of zero. Additionally, we have created a tool in the srs-bench to replay pcapng files captured by tcpdump or Wireshark. We have also added utest using mprotect and asan to detect any memory corruption. It is important to note that this bug has been fixed in versions 4.0.2716477f31004
and 5.0.170939f6b484b
. This patch specifically addresses the issue in SRS 6.0. Please be aware that there is another commit related to this bug that partially fixes the issue but still leaves a small problem for asan to detect memory corruption. This commit,577cd299e1
, only ignores empty NALU packets but still reads beyond the memory. --------- Co-authored-by: chundonglinlin <chundonglinlin@163.com>
This commit is contained in:
parent
e19efe0bcd
commit
73dd8af4c9
182 changed files with 46111 additions and 3914 deletions
286
trunk/3rdparty/srs-bench/vendor/github.com/google/gopacket/pcapgo/capture.go
generated
vendored
Normal file
286
trunk/3rdparty/srs-bench/vendor/github.com/google/gopacket/pcapgo/capture.go
generated
vendored
Normal file
|
@ -0,0 +1,286 @@
|
|||
// Copyright 2012 Google, Inc. All rights reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the LICENSE file in the root of the source
|
||||
// tree.
|
||||
// +build linux,go1.9
|
||||
|
||||
package pcapgo
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"runtime"
|
||||
"sync"
|
||||
"syscall"
|
||||
"time"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/net/bpf"
|
||||
"golang.org/x/sys/unix"
|
||||
|
||||
"github.com/google/gopacket"
|
||||
)
|
||||
|
||||
var hdrLen = unix.CmsgSpace(0)
|
||||
var auxLen = unix.CmsgSpace(int(unsafe.Sizeof(unix.TpacketAuxdata{})))
|
||||
var timensLen = unix.CmsgSpace(int(unsafe.Sizeof(unix.Timespec{})))
|
||||
var timeLen = unix.CmsgSpace(int(unsafe.Sizeof(unix.Timeval{})))
|
||||
|
||||
func htons(data uint16) uint16 { return data<<8 | data>>8 }
|
||||
|
||||
// EthernetHandle holds shared buffers and file descriptor of af_packet socket
|
||||
type EthernetHandle struct {
|
||||
fd int
|
||||
buffer []byte
|
||||
oob []byte
|
||||
ancil []interface{}
|
||||
mu sync.Mutex
|
||||
intf int
|
||||
addr net.HardwareAddr
|
||||
}
|
||||
|
||||
// readOne reads a packet from the handle and returns a capture info + vlan info
|
||||
func (h *EthernetHandle) readOne() (ci gopacket.CaptureInfo, vlan int, haveVlan bool, err error) {
|
||||
// we could use unix.Recvmsg, but that does a memory allocation (for the returned sockaddr) :(
|
||||
var msg unix.Msghdr
|
||||
var sa unix.RawSockaddrLinklayer
|
||||
|
||||
msg.Name = (*byte)(unsafe.Pointer(&sa))
|
||||
msg.Namelen = uint32(unsafe.Sizeof(sa))
|
||||
|
||||
var iov unix.Iovec
|
||||
if len(h.buffer) > 0 {
|
||||
iov.Base = &h.buffer[0]
|
||||
iov.SetLen(len(h.buffer))
|
||||
}
|
||||
msg.Iov = &iov
|
||||
msg.Iovlen = 1
|
||||
|
||||
if len(h.oob) > 0 {
|
||||
msg.Control = &h.oob[0]
|
||||
msg.SetControllen(len(h.oob))
|
||||
}
|
||||
|
||||
// use msg_trunc so we know packet size without auxdata, which might be missing
|
||||
n, _, e := syscall.Syscall(unix.SYS_RECVMSG, uintptr(h.fd), uintptr(unsafe.Pointer(&msg)), uintptr(unix.MSG_TRUNC))
|
||||
|
||||
if e != 0 {
|
||||
return gopacket.CaptureInfo{}, 0, false, fmt.Errorf("couldn't read packet: %s", e)
|
||||
}
|
||||
|
||||
if sa.Family == unix.AF_PACKET {
|
||||
ci.InterfaceIndex = int(sa.Ifindex)
|
||||
} else {
|
||||
ci.InterfaceIndex = h.intf
|
||||
}
|
||||
|
||||
// custom aux parsing so we don't allocate stuff (unix.ParseSocketControlMessage allocates a slice)
|
||||
// we're getting at most 2 cmsgs anyway and know which ones they are (auxdata + timestamp(ns))
|
||||
oob := h.oob[:msg.Controllen]
|
||||
gotAux := false
|
||||
|
||||
for len(oob) > hdrLen { // > hdrLen, because we also need something after the cmsg header
|
||||
hdr := (*unix.Cmsghdr)(unsafe.Pointer(&oob[0]))
|
||||
switch {
|
||||
case hdr.Level == unix.SOL_PACKET && hdr.Type == unix.PACKET_AUXDATA && len(oob) >= auxLen:
|
||||
aux := (*unix.TpacketAuxdata)(unsafe.Pointer(&oob[hdrLen]))
|
||||
ci.CaptureLength = int(n)
|
||||
ci.Length = int(aux.Len)
|
||||
vlan = int(aux.Vlan_tci)
|
||||
haveVlan = (aux.Status & unix.TP_STATUS_VLAN_VALID) != 0
|
||||
gotAux = true
|
||||
case hdr.Level == unix.SOL_SOCKET && hdr.Type == unix.SO_TIMESTAMPNS && len(oob) >= timensLen:
|
||||
tstamp := (*unix.Timespec)(unsafe.Pointer(&oob[hdrLen]))
|
||||
ci.Timestamp = time.Unix(int64(tstamp.Sec), int64(tstamp.Nsec))
|
||||
case hdr.Level == unix.SOL_SOCKET && hdr.Type == unix.SO_TIMESTAMP && len(oob) >= timeLen:
|
||||
tstamp := (*unix.Timeval)(unsafe.Pointer(&oob[hdrLen]))
|
||||
ci.Timestamp = time.Unix(int64(tstamp.Sec), int64(tstamp.Usec)*1000)
|
||||
}
|
||||
oob = oob[unix.CmsgSpace(int(hdr.Len))-hdrLen:]
|
||||
}
|
||||
|
||||
if !gotAux {
|
||||
// fallback for no aux cmsg
|
||||
ci.CaptureLength = int(n)
|
||||
ci.Length = int(n)
|
||||
haveVlan = false
|
||||
}
|
||||
|
||||
// fix up capture length if we needed to truncate
|
||||
if ci.CaptureLength > len(h.buffer) {
|
||||
ci.CaptureLength = len(h.buffer)
|
||||
}
|
||||
|
||||
if ci.Timestamp.IsZero() {
|
||||
// we got no timestamp info -> emulate it
|
||||
ci.Timestamp = time.Now()
|
||||
}
|
||||
|
||||
return ci, vlan, haveVlan, nil
|
||||
}
|
||||
|
||||
// ReadPacketData implements gopacket.PacketDataSource. If this was captured on a vlan, the vlan id will be in the AncillaryData[0]
|
||||
func (h *EthernetHandle) ReadPacketData() ([]byte, gopacket.CaptureInfo, error) {
|
||||
h.mu.Lock()
|
||||
ci, vlan, haveVlan, err := h.readOne()
|
||||
if err != nil {
|
||||
h.mu.Unlock()
|
||||
return nil, gopacket.CaptureInfo{}, fmt.Errorf("couldn't read packet data: %s", err)
|
||||
}
|
||||
|
||||
b := make([]byte, ci.CaptureLength)
|
||||
copy(b, h.buffer)
|
||||
h.mu.Unlock()
|
||||
|
||||
if haveVlan {
|
||||
ci.AncillaryData = []interface{}{vlan}
|
||||
|
||||
}
|
||||
|
||||
return b, ci, nil
|
||||
}
|
||||
|
||||
// ZeroCopyReadPacketData implements gopacket.ZeroCopyPacketDataSource. If this was captured on a vlan, the vlan id will be in the AncillaryData[0].
|
||||
// This function does not allocate memory. Beware that the next call to ZeroCopyReadPacketData will overwrite existing slices (returned data AND AncillaryData)!
|
||||
// Due to shared buffers this must not be called concurrently
|
||||
func (h *EthernetHandle) ZeroCopyReadPacketData() ([]byte, gopacket.CaptureInfo, error) {
|
||||
ci, vlan, haveVlan, err := h.readOne()
|
||||
if err != nil {
|
||||
return nil, gopacket.CaptureInfo{}, fmt.Errorf("couldn't read packet data: %s", err)
|
||||
}
|
||||
|
||||
if haveVlan {
|
||||
h.ancil[0] = vlan
|
||||
ci.AncillaryData = h.ancil
|
||||
}
|
||||
|
||||
return h.buffer[:ci.CaptureLength], ci, nil
|
||||
}
|
||||
|
||||
// Close closes the underlying socket
|
||||
func (h *EthernetHandle) Close() {
|
||||
if h.fd != -1 {
|
||||
unix.Close(h.fd)
|
||||
h.fd = -1
|
||||
runtime.SetFinalizer(h, nil)
|
||||
}
|
||||
}
|
||||
|
||||
// SetCaptureLength sets the maximum capture length to the given value
|
||||
func (h *EthernetHandle) SetCaptureLength(len int) error {
|
||||
if len < 0 {
|
||||
return fmt.Errorf("illegal capture length %d. Must be at least 0", len)
|
||||
}
|
||||
h.buffer = make([]byte, len)
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetCaptureLength returns the maximum capture length
|
||||
func (h *EthernetHandle) GetCaptureLength() int {
|
||||
return len(h.buffer)
|
||||
}
|
||||
|
||||
// SetBPF attaches the given BPF filter to the socket. After this, only the packets for which the filter returns a value greater than zero are received.
|
||||
// If a filter was already attached, it will be overwritten. To remove the filter, provide an empty slice.
|
||||
func (h *EthernetHandle) SetBPF(filter []bpf.RawInstruction) error {
|
||||
if len(filter) == 0 {
|
||||
return unix.SetsockoptInt(h.fd, unix.SOL_SOCKET, unix.SO_DETACH_FILTER, 0)
|
||||
}
|
||||
f := make([]unix.SockFilter, len(filter))
|
||||
for i := range filter {
|
||||
f[i].Code = filter[i].Op
|
||||
f[i].Jf = filter[i].Jf
|
||||
f[i].Jt = filter[i].Jt
|
||||
f[i].K = filter[i].K
|
||||
}
|
||||
fprog := &unix.SockFprog{
|
||||
Len: uint16(len(filter)),
|
||||
Filter: &f[0],
|
||||
}
|
||||
return unix.SetsockoptSockFprog(h.fd, unix.SOL_SOCKET, unix.SO_ATTACH_FILTER, fprog)
|
||||
}
|
||||
|
||||
// LocalAddr returns the local network address
|
||||
func (h *EthernetHandle) LocalAddr() net.HardwareAddr {
|
||||
// Hardware Address might have changed. Fetch new one and fall back to the stored one if fetching interface fails
|
||||
intf, err := net.InterfaceByIndex(h.intf)
|
||||
if err == nil {
|
||||
h.addr = intf.HardwareAddr
|
||||
}
|
||||
return h.addr
|
||||
}
|
||||
|
||||
// SetPromiscuous sets promiscous mode to the required value. If it is enabled, traffic not destined for the interface will also be captured.
|
||||
func (h *EthernetHandle) SetPromiscuous(b bool) error {
|
||||
mreq := unix.PacketMreq{
|
||||
Ifindex: int32(h.intf),
|
||||
Type: unix.PACKET_MR_PROMISC,
|
||||
}
|
||||
|
||||
opt := unix.PACKET_ADD_MEMBERSHIP
|
||||
if !b {
|
||||
opt = unix.PACKET_DROP_MEMBERSHIP
|
||||
}
|
||||
|
||||
return unix.SetsockoptPacketMreq(h.fd, unix.SOL_PACKET, opt, &mreq)
|
||||
}
|
||||
|
||||
// Stats returns number of packets and dropped packets. This will be the number of packets/dropped packets since the last call to stats (not the cummulative sum!).
|
||||
func (h *EthernetHandle) Stats() (*unix.TpacketStats, error) {
|
||||
return unix.GetsockoptTpacketStats(h.fd, unix.SOL_PACKET, unix.PACKET_STATISTICS)
|
||||
}
|
||||
|
||||
// NewEthernetHandle implements pcap.OpenLive for network devices.
|
||||
// If you want better performance have a look at github.com/google/gopacket/afpacket.
|
||||
// SetCaptureLength can be used to limit the maximum capture length.
|
||||
func NewEthernetHandle(ifname string) (*EthernetHandle, error) {
|
||||
intf, err := net.InterfaceByName(ifname)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("couldn't query interface %s: %s", ifname, err)
|
||||
}
|
||||
|
||||
fd, err := unix.Socket(unix.AF_PACKET, unix.SOCK_RAW, int(htons(unix.ETH_P_ALL)))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("couldn't open packet socket: %s", err)
|
||||
}
|
||||
|
||||
addr := unix.SockaddrLinklayer{
|
||||
Protocol: htons(unix.ETH_P_ALL),
|
||||
Ifindex: intf.Index,
|
||||
}
|
||||
|
||||
if err := unix.Bind(fd, &addr); err != nil {
|
||||
return nil, fmt.Errorf("couldn't bind to interface %s: %s", ifname, err)
|
||||
}
|
||||
|
||||
ooblen := 0
|
||||
|
||||
if err := unix.SetsockoptInt(fd, unix.SOL_PACKET, unix.PACKET_AUXDATA, 1); err != nil {
|
||||
// we can't get auxdata -> no vlan info
|
||||
} else {
|
||||
ooblen += auxLen
|
||||
}
|
||||
|
||||
if err := unix.SetsockoptInt(fd, unix.SOL_SOCKET, unix.SO_TIMESTAMPNS, 1); err != nil {
|
||||
// no nanosecond resolution :( -> try ms
|
||||
if err := unix.SetsockoptInt(fd, unix.SOL_SOCKET, unix.SO_TIMESTAMP, 1); err != nil {
|
||||
// if this doesn't work we well use time.Now() -> ignore errors here
|
||||
} else {
|
||||
ooblen += timeLen
|
||||
}
|
||||
} else {
|
||||
ooblen += timensLen
|
||||
}
|
||||
|
||||
handle := &EthernetHandle{
|
||||
fd: fd,
|
||||
buffer: make([]byte, intf.MTU),
|
||||
oob: make([]byte, ooblen),
|
||||
ancil: make([]interface{}, 1),
|
||||
intf: intf.Index,
|
||||
addr: intf.HardwareAddr,
|
||||
}
|
||||
runtime.SetFinalizer(handle, (*EthernetHandle).Close)
|
||||
return handle, nil
|
||||
}
|
63
trunk/3rdparty/srs-bench/vendor/github.com/google/gopacket/pcapgo/doc.go
generated
vendored
Normal file
63
trunk/3rdparty/srs-bench/vendor/github.com/google/gopacket/pcapgo/doc.go
generated
vendored
Normal file
|
@ -0,0 +1,63 @@
|
|||
// Copyright 2018 The GoPacket Authors. All rights reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the LICENSE file in the root of the source
|
||||
// tree.
|
||||
|
||||
/*
|
||||
Package pcapgo provides some native PCAP support, not requiring C libpcap to be installed.
|
||||
|
||||
Overview
|
||||
|
||||
This package contains implementations for native PCAP support. Currently supported are
|
||||
|
||||
* pcap-files read/write: Reader, Writer
|
||||
* pcapng-files read/write: NgReader, NgWriter
|
||||
* raw socket capture (linux only): EthernetHandle
|
||||
|
||||
Basic Usage pcapng
|
||||
|
||||
Pcapng files can be read and written. Reading supports both big and little endian files, packet blocks,
|
||||
simple packet blocks, enhanced packets blocks, interface blocks, and interface statistics blocks. All
|
||||
the options also by Wireshark are supported. The default reader options match libpcap behaviour. Have
|
||||
a look at NgReaderOptions for more advanced usage. Both ReadPacketData and ZeroCopyReadPacketData is
|
||||
supported (which means PacketDataSource and ZeroCopyPacketDataSource is supported).
|
||||
|
||||
f, err := os.Open("somefile.pcapng")
|
||||
if err != nil {
|
||||
...
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
r, err := NewNgReader(f, DefaultNgReaderOptions)
|
||||
if err != nil {
|
||||
...
|
||||
}
|
||||
|
||||
data, ci, err := r.ReadPacketData()
|
||||
...
|
||||
|
||||
Write supports only little endian, enhanced packets blocks, interface blocks, and interface statistics
|
||||
blocks. The same options as with writing are supported. Interface timestamp resolution is fixed to
|
||||
10^-9s to match time.Time. Any other values are ignored. Upon creating a writer, a section, and an
|
||||
interface block is automatically written. Additional interfaces can be added at any time. Since
|
||||
the writer uses a bufio.Writer internally, Flush must be called before closing the file! Have a look
|
||||
at NewNgWriterInterface for more advanced usage.
|
||||
|
||||
f, err := os.Create("somefile.pcapng")
|
||||
if err != nil {
|
||||
...
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
r, err = NewNgWriter(f, layers.LinkTypeEthernet)
|
||||
if err != nil {
|
||||
...
|
||||
}
|
||||
defer r.Flush()
|
||||
|
||||
err = r.WritePacket(ci, data)
|
||||
...
|
||||
|
||||
*/
|
||||
package pcapgo
|
606
trunk/3rdparty/srs-bench/vendor/github.com/google/gopacket/pcapgo/ngread.go
generated
vendored
Normal file
606
trunk/3rdparty/srs-bench/vendor/github.com/google/gopacket/pcapgo/ngread.go
generated
vendored
Normal file
|
@ -0,0 +1,606 @@
|
|||
// Copyright 2018 The GoPacket Authors. All rights reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the LICENSE file in the root of the source
|
||||
// tree.
|
||||
|
||||
package pcapgo
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"time"
|
||||
|
||||
"github.com/google/gopacket"
|
||||
"github.com/google/gopacket/layers"
|
||||
)
|
||||
|
||||
// NgReaderOptions holds options for reading a pcapng file
|
||||
type NgReaderOptions struct {
|
||||
// WantMixedLinkType enables reading a pcapng file containing multiple interfaces with varying link types. If false all link types must match, which is the libpcap behaviour and LinkType returns the link type of the first interface.
|
||||
// If true the link type of the packet is also exposed via ci.AncillaryData[0].
|
||||
WantMixedLinkType bool
|
||||
// ErrorOnMismatchingLinkType enables returning an error if a packet with a link type not matching the first interface is encountered and WantMixedLinkType == false.
|
||||
// If false packets those packets are just silently ignored, which is the libpcap behaviour.
|
||||
ErrorOnMismatchingLinkType bool
|
||||
// SkipUnknownVersion enables automatically skipping sections with an unknown version, which is recommended by the pcapng standard. Otherwise ErrVersionMismatch is returned.
|
||||
SkipUnknownVersion bool
|
||||
// SectionEndCallback gets called at the end of a section (execept for the last section, which is ends on EOF). The current list of interfaces and additional section information is provided.
|
||||
// This is a good way to read interface statistics.
|
||||
SectionEndCallback func([]NgInterface, NgSectionInfo)
|
||||
// StatisticsCallback is called when a interface statistics block is read. The interface id and the read statistics are provided.
|
||||
StatisticsCallback func(int, NgInterfaceStatistics)
|
||||
}
|
||||
|
||||
// DefaultNgReaderOptions provides sane defaults for a pcapng reader.
|
||||
var DefaultNgReaderOptions = NgReaderOptions{}
|
||||
|
||||
// NgReader wraps an underlying bufio.NgReader to read packet data in pcapng.
|
||||
type NgReader struct {
|
||||
r *bufio.Reader
|
||||
options NgReaderOptions
|
||||
sectionInfo NgSectionInfo
|
||||
linkType layers.LinkType
|
||||
ifaces []NgInterface
|
||||
currentBlock ngBlock
|
||||
currentOption ngOption
|
||||
buf [24]byte
|
||||
packetBuf []byte
|
||||
ci gopacket.CaptureInfo
|
||||
ancil [1]interface{}
|
||||
blen int
|
||||
firstSectionFound bool
|
||||
activeSection bool
|
||||
bigEndian bool
|
||||
}
|
||||
|
||||
// NewNgReader initializes a new writer, reads the first section header, and if necessary according to the options the first interface.
|
||||
func NewNgReader(r io.Reader, options NgReaderOptions) (*NgReader, error) {
|
||||
ret := &NgReader{
|
||||
r: bufio.NewReader(r),
|
||||
currentOption: ngOption{
|
||||
value: make([]byte, 1024),
|
||||
},
|
||||
options: options,
|
||||
}
|
||||
|
||||
//pcapng _must_ start with a section header
|
||||
if err := ret.readBlock(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if ret.currentBlock.typ != ngBlockTypeSectionHeader {
|
||||
return nil, fmt.Errorf("Unknown magic %x", ret.currentBlock.typ)
|
||||
}
|
||||
|
||||
if err := ret.readSectionHeader(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
// First a couple of helper functions to speed things up
|
||||
|
||||
// This is way faster than calling io.ReadFull since io.ReadFull needs an itab lookup, does an additional function call into ReadAtLeast, and ReadAtLeast does additional stuff we don't need
|
||||
// Additionally this removes the bounds check compared to io.ReadFull due to the use of uint
|
||||
func (r *NgReader) readBytes(buffer []byte) error {
|
||||
n := uint(0)
|
||||
for n < uint(len(buffer)) {
|
||||
nn, err := r.r.Read(buffer[n:])
|
||||
n += uint(nn)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// The following functions make the binary.* functions inlineable (except for getUint64, which is too big, but not in any hot path anyway)
|
||||
// Compared to storing binary.*Endian in a binary.ByteOrder this shaves off about 20% for (ZeroCopy)ReadPacketData, which is caused by the needed itab lookup + indirect go call
|
||||
func (r *NgReader) getUint16(buffer []byte) uint16 {
|
||||
if r.bigEndian {
|
||||
return binary.BigEndian.Uint16(buffer)
|
||||
}
|
||||
return binary.LittleEndian.Uint16(buffer)
|
||||
}
|
||||
|
||||
func (r *NgReader) getUint32(buffer []byte) uint32 {
|
||||
if r.bigEndian {
|
||||
return binary.BigEndian.Uint32(buffer)
|
||||
}
|
||||
return binary.LittleEndian.Uint32(buffer)
|
||||
}
|
||||
|
||||
func (r *NgReader) getUint64(buffer []byte) uint64 {
|
||||
if r.bigEndian {
|
||||
return binary.BigEndian.Uint64(buffer)
|
||||
}
|
||||
return binary.LittleEndian.Uint64(buffer)
|
||||
}
|
||||
|
||||
// Now the pcapng implementation
|
||||
|
||||
// readBlock reads a the blocktype and length from the file. If the type is a section header, endianess is also read.
|
||||
func (r *NgReader) readBlock() error {
|
||||
if err := r.readBytes(r.buf[0:8]); err != nil {
|
||||
return err
|
||||
}
|
||||
r.currentBlock.typ = ngBlockType(r.getUint32(r.buf[0:4]))
|
||||
// The next part is a bit fucked up since a section header could change the endianess...
|
||||
// So first read then length just into a buffer, check if its a section header and then do the endianess part...
|
||||
if r.currentBlock.typ == ngBlockTypeSectionHeader {
|
||||
if err := r.readBytes(r.buf[8:12]); err != nil {
|
||||
return err
|
||||
}
|
||||
if binary.BigEndian.Uint32(r.buf[8:12]) == ngByteOrderMagic {
|
||||
r.bigEndian = true
|
||||
} else if binary.LittleEndian.Uint32(r.buf[8:12]) == ngByteOrderMagic {
|
||||
r.bigEndian = false
|
||||
} else {
|
||||
return errors.New("Wrong byte order value in Section Header")
|
||||
}
|
||||
// Set length to remaining length (length - (type + lengthfield = 8) - 4 for byteOrderMagic)
|
||||
r.currentBlock.length = r.getUint32(r.buf[4:8]) - 8 - 4
|
||||
return nil
|
||||
}
|
||||
// Set length to remaining length (length - (type + lengthfield = 8)
|
||||
r.currentBlock.length = r.getUint32(r.buf[4:8]) - 8
|
||||
return nil
|
||||
}
|
||||
|
||||
// readOption reads a single arbitrary option (type and value). If there is no space left for options and end of options is missing, it is faked.
|
||||
func (r *NgReader) readOption() error {
|
||||
if r.currentBlock.length == 4 {
|
||||
// no more options
|
||||
r.currentOption.code = ngOptionCodeEndOfOptions
|
||||
return nil
|
||||
}
|
||||
if err := r.readBytes(r.buf[:4]); err != nil {
|
||||
return err
|
||||
}
|
||||
r.currentBlock.length -= 4
|
||||
r.currentOption.code = ngOptionCode(r.getUint16(r.buf[:2]))
|
||||
length := r.getUint16(r.buf[2:4])
|
||||
if r.currentOption.code == ngOptionCodeEndOfOptions {
|
||||
if length != 0 {
|
||||
return errors.New("End of Options must be zero length")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
if length != 0 {
|
||||
if length < uint16(cap(r.currentOption.value)) {
|
||||
r.currentOption.value = r.currentOption.value[:length]
|
||||
} else {
|
||||
r.currentOption.value = make([]byte, length)
|
||||
}
|
||||
if err := r.readBytes(r.currentOption.value); err != nil {
|
||||
return err
|
||||
}
|
||||
//consume padding
|
||||
padding := length % 4
|
||||
if padding > 0 {
|
||||
padding = 4 - padding
|
||||
if _, err := r.r.Discard(int(padding)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
r.currentBlock.length -= uint32(length + padding)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// readSectionHeader parses the full section header and implements section skipping in case of version mismatch
|
||||
// if needed, the first interface is read
|
||||
func (r *NgReader) readSectionHeader() error {
|
||||
if r.options.SectionEndCallback != nil && r.activeSection {
|
||||
interfaces := make([]NgInterface, len(r.ifaces))
|
||||
for i := range r.ifaces {
|
||||
interfaces[i] = r.ifaces[i]
|
||||
}
|
||||
r.options.SectionEndCallback(interfaces, r.sectionInfo)
|
||||
}
|
||||
// clear the interfaces
|
||||
r.ifaces = r.ifaces[:0]
|
||||
r.activeSection = false
|
||||
|
||||
RESTART:
|
||||
// read major, minor, section length
|
||||
if err := r.readBytes(r.buf[:12]); err != nil {
|
||||
return err
|
||||
}
|
||||
r.currentBlock.length -= 12
|
||||
|
||||
vMajor := r.getUint16(r.buf[0:2])
|
||||
vMinor := r.getUint16(r.buf[2:4])
|
||||
if vMajor != ngVersionMajor || vMinor != ngVersionMinor {
|
||||
if !r.options.SkipUnknownVersion {
|
||||
// Well the standard actually says to skip unknown version section headers,
|
||||
// but this would mean user would be kept in the dark about whats going on...
|
||||
return ErrNgVersionMismatch
|
||||
}
|
||||
if _, err := r.r.Discard(int(r.currentBlock.length)); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := r.skipSection(); err != nil {
|
||||
return err
|
||||
}
|
||||
goto RESTART
|
||||
}
|
||||
|
||||
var section NgSectionInfo
|
||||
|
||||
OPTIONS:
|
||||
for {
|
||||
if err := r.readOption(); err != nil {
|
||||
return err
|
||||
}
|
||||
switch r.currentOption.code {
|
||||
case ngOptionCodeEndOfOptions:
|
||||
break OPTIONS
|
||||
case ngOptionCodeComment:
|
||||
section.Comment = string(r.currentOption.value)
|
||||
case ngOptionCodeHardware:
|
||||
section.Hardware = string(r.currentOption.value)
|
||||
case ngOptionCodeOS:
|
||||
section.OS = string(r.currentOption.value)
|
||||
case ngOptionCodeUserApplication:
|
||||
section.Application = string(r.currentOption.value)
|
||||
}
|
||||
}
|
||||
|
||||
if _, err := r.r.Discard(int(r.currentBlock.length)); err != nil {
|
||||
return err
|
||||
}
|
||||
r.activeSection = true
|
||||
r.sectionInfo = section
|
||||
|
||||
if !r.options.WantMixedLinkType {
|
||||
// If we don't want mixed link type, we need the first interface to fill Reader.LinkType()
|
||||
// This handles most of the pcapngs out there, since they start with an IDB
|
||||
if err := r.firstInterface(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// skipSection skips blocks until the next section
|
||||
func (r *NgReader) skipSection() error {
|
||||
for {
|
||||
if err := r.readBlock(); err != nil {
|
||||
return err
|
||||
}
|
||||
if r.currentBlock.typ == ngBlockTypeSectionHeader {
|
||||
return nil
|
||||
}
|
||||
if _, err := r.r.Discard(int(r.currentBlock.length)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// SkipSection skips the contents of the rest of the current section and reads the next section header.
|
||||
func (r *NgReader) SkipSection() error {
|
||||
if err := r.skipSection(); err != nil {
|
||||
return err
|
||||
}
|
||||
return r.readSectionHeader()
|
||||
}
|
||||
|
||||
// firstInterface reads the first interface from the section and panics if a packet is encountered.
|
||||
func (r *NgReader) firstInterface() error {
|
||||
for {
|
||||
if err := r.readBlock(); err != nil {
|
||||
return err
|
||||
}
|
||||
switch r.currentBlock.typ {
|
||||
case ngBlockTypeInterfaceDescriptor:
|
||||
if err := r.readInterfaceDescriptor(); err != nil {
|
||||
return err
|
||||
}
|
||||
if !r.firstSectionFound {
|
||||
r.linkType = r.ifaces[0].LinkType
|
||||
r.firstSectionFound = true
|
||||
} else if r.linkType != r.ifaces[0].LinkType {
|
||||
if r.options.ErrorOnMismatchingLinkType {
|
||||
return ErrNgLinkTypeMismatch
|
||||
}
|
||||
continue
|
||||
}
|
||||
return nil
|
||||
case ngBlockTypePacket, ngBlockTypeEnhancedPacket, ngBlockTypeSimplePacket, ngBlockTypeInterfaceStatistics:
|
||||
return errors.New("A section must have an interface before a packet block")
|
||||
}
|
||||
if _, err := r.r.Discard(int(r.currentBlock.length)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// readInterfaceDescriptor parses an interface descriptor, prepares timing calculation, and adds the interface details to the current list
|
||||
func (r *NgReader) readInterfaceDescriptor() error {
|
||||
if err := r.readBytes(r.buf[:8]); err != nil {
|
||||
return err
|
||||
}
|
||||
r.currentBlock.length -= 8
|
||||
var intf NgInterface
|
||||
intf.LinkType = layers.LinkType(r.getUint16(r.buf[:2]))
|
||||
intf.SnapLength = r.getUint32(r.buf[4:8])
|
||||
|
||||
OPTIONS:
|
||||
for {
|
||||
if err := r.readOption(); err != nil {
|
||||
return err
|
||||
}
|
||||
switch r.currentOption.code {
|
||||
case ngOptionCodeEndOfOptions:
|
||||
break OPTIONS
|
||||
case ngOptionCodeInterfaceName:
|
||||
intf.Name = string(r.currentOption.value)
|
||||
case ngOptionCodeComment:
|
||||
intf.Comment = string(r.currentOption.value)
|
||||
case ngOptionCodeInterfaceDescription:
|
||||
intf.Description = string(r.currentOption.value)
|
||||
case ngOptionCodeInterfaceFilter:
|
||||
// ignore filter type (first byte) since it is not specified
|
||||
intf.Filter = string(r.currentOption.value[1:])
|
||||
case ngOptionCodeInterfaceOS:
|
||||
intf.OS = string(r.currentOption.value)
|
||||
case ngOptionCodeInterfaceTimestampOffset:
|
||||
intf.TimestampOffset = r.getUint64(r.currentOption.value[:8])
|
||||
case ngOptionCodeInterfaceTimestampResolution:
|
||||
intf.TimestampResolution = NgResolution(r.currentOption.value[0])
|
||||
}
|
||||
}
|
||||
if _, err := r.r.Discard(int(r.currentBlock.length)); err != nil {
|
||||
return err
|
||||
}
|
||||
if intf.TimestampResolution == 0 {
|
||||
intf.TimestampResolution = 6
|
||||
}
|
||||
|
||||
//parse options
|
||||
if intf.TimestampResolution.Binary() {
|
||||
//negative power of 2
|
||||
intf.secondMask = 1 << intf.TimestampResolution.Exponent()
|
||||
} else {
|
||||
//negative power of 10
|
||||
intf.secondMask = 1
|
||||
for j := uint8(0); j < intf.TimestampResolution.Exponent(); j++ {
|
||||
intf.secondMask *= 10
|
||||
}
|
||||
}
|
||||
intf.scaleDown = 1
|
||||
intf.scaleUp = 1
|
||||
if intf.secondMask < 1e9 {
|
||||
intf.scaleUp = 1e9 / intf.secondMask
|
||||
} else {
|
||||
intf.scaleDown = intf.secondMask / 1e9
|
||||
}
|
||||
r.ifaces = append(r.ifaces, intf)
|
||||
return nil
|
||||
}
|
||||
|
||||
// convertTime adds offset + shifts the given time value according to the given interface
|
||||
func (r *NgReader) convertTime(ifaceID int, ts uint64) (int64, int64) {
|
||||
iface := r.ifaces[ifaceID]
|
||||
return int64(ts/iface.secondMask + iface.TimestampOffset), int64(ts % iface.secondMask * iface.scaleUp / iface.scaleDown)
|
||||
}
|
||||
|
||||
// readInterfaceStatistics updates the statistics of the given interface
|
||||
func (r *NgReader) readInterfaceStatistics() error {
|
||||
if err := r.readBytes(r.buf[:12]); err != nil {
|
||||
return err
|
||||
}
|
||||
r.currentBlock.length -= 12
|
||||
ifaceID := int(r.getUint32(r.buf[:4]))
|
||||
ts := uint64(r.getUint32(r.buf[4:8]))<<32 | uint64(r.getUint32(r.buf[8:12]))
|
||||
if int(ifaceID) >= len(r.ifaces) {
|
||||
return fmt.Errorf("Interface id %d not present in section (have only %d interfaces)", ifaceID, len(r.ifaces))
|
||||
}
|
||||
stats := &r.ifaces[ifaceID].Statistics
|
||||
*stats = ngEmptyStatistics
|
||||
stats.LastUpdate = time.Unix(r.convertTime(ifaceID, ts)).UTC()
|
||||
|
||||
OPTIONS:
|
||||
for {
|
||||
if err := r.readOption(); err != nil {
|
||||
return err
|
||||
}
|
||||
switch r.currentOption.code {
|
||||
case ngOptionCodeEndOfOptions:
|
||||
break OPTIONS
|
||||
case ngOptionCodeComment:
|
||||
stats.Comment = string(r.currentOption.value)
|
||||
case ngOptionCodeInterfaceStatisticsStartTime:
|
||||
ts = uint64(r.getUint32(r.currentOption.value[:4]))<<32 | uint64(r.getUint32(r.currentOption.value[4:8]))
|
||||
stats.StartTime = time.Unix(r.convertTime(ifaceID, ts)).UTC()
|
||||
case ngOptionCodeInterfaceStatisticsEndTime:
|
||||
ts = uint64(r.getUint32(r.currentOption.value[:4]))<<32 | uint64(r.getUint32(r.currentOption.value[4:8]))
|
||||
stats.EndTime = time.Unix(r.convertTime(ifaceID, ts)).UTC()
|
||||
case ngOptionCodeInterfaceStatisticsInterfaceReceived:
|
||||
stats.PacketsReceived = r.getUint64(r.currentOption.value[:8])
|
||||
case ngOptionCodeInterfaceStatisticsInterfaceDropped:
|
||||
stats.PacketsDropped = r.getUint64(r.currentOption.value[:8])
|
||||
}
|
||||
}
|
||||
if _, err := r.r.Discard(int(r.currentBlock.length)); err != nil {
|
||||
return err
|
||||
}
|
||||
if r.options.StatisticsCallback != nil {
|
||||
r.options.StatisticsCallback(ifaceID, *stats)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// readPacketHeader looks for a packet (enhanced, simple, or packet) and parses the header.
|
||||
// If an interface descriptor, an interface statistics block, or a section header is encountered, those are handled accordingly.
|
||||
// All other block types are skipped. New block types must be added here.
|
||||
func (r *NgReader) readPacketHeader() error {
|
||||
RESTART:
|
||||
FIND_PACKET:
|
||||
for {
|
||||
if err := r.readBlock(); err != nil {
|
||||
return err
|
||||
}
|
||||
switch r.currentBlock.typ {
|
||||
case ngBlockTypeEnhancedPacket:
|
||||
if err := r.readBytes(r.buf[:20]); err != nil {
|
||||
return err
|
||||
}
|
||||
r.currentBlock.length -= 20
|
||||
r.ci.InterfaceIndex = int(r.getUint32(r.buf[:4]))
|
||||
if r.ci.InterfaceIndex >= len(r.ifaces) {
|
||||
return fmt.Errorf("Interface id %d not present in section (have only %d interfaces)", r.ci.InterfaceIndex, len(r.ifaces))
|
||||
}
|
||||
r.ci.Timestamp = time.Unix(r.convertTime(r.ci.InterfaceIndex, uint64(r.getUint32(r.buf[4:8]))<<32|uint64(r.getUint32(r.buf[8:12])))).UTC()
|
||||
r.ci.CaptureLength = int(r.getUint32(r.buf[12:16]))
|
||||
r.ci.Length = int(r.getUint32(r.buf[16:20]))
|
||||
break FIND_PACKET
|
||||
case ngBlockTypeSimplePacket:
|
||||
if err := r.readBytes(r.buf[:4]); err != nil {
|
||||
return err
|
||||
}
|
||||
r.currentBlock.length -= 4
|
||||
r.ci.Timestamp = time.Time{}
|
||||
r.ci.InterfaceIndex = 0
|
||||
r.ci.Length = int(r.getUint32(r.buf[:4]))
|
||||
r.ci.CaptureLength = r.ci.Length
|
||||
if len(r.ifaces) == 0 {
|
||||
return errors.New("At least one Interface is needed for a packet")
|
||||
}
|
||||
if r.ifaces[0].SnapLength != 0 && uint32(r.ci.CaptureLength) > r.ifaces[0].SnapLength {
|
||||
r.ci.CaptureLength = int(r.ifaces[0].SnapLength)
|
||||
}
|
||||
break FIND_PACKET
|
||||
case ngBlockTypeInterfaceDescriptor:
|
||||
if err := r.readInterfaceDescriptor(); err != nil {
|
||||
return err
|
||||
}
|
||||
case ngBlockTypeInterfaceStatistics:
|
||||
if err := r.readInterfaceStatistics(); err != nil {
|
||||
return err
|
||||
}
|
||||
case ngBlockTypeSectionHeader:
|
||||
if err := r.readSectionHeader(); err != nil {
|
||||
return err
|
||||
}
|
||||
case ngBlockTypePacket:
|
||||
if err := r.readBytes(r.buf[:20]); err != nil {
|
||||
return err
|
||||
}
|
||||
r.currentBlock.length -= 20
|
||||
r.ci.InterfaceIndex = int(r.getUint16(r.buf[0:2]))
|
||||
if r.ci.InterfaceIndex >= len(r.ifaces) {
|
||||
return fmt.Errorf("Interface id %d not present in section (have only %d interfaces)", r.ci.InterfaceIndex, len(r.ifaces))
|
||||
}
|
||||
r.ci.Timestamp = time.Unix(r.convertTime(r.ci.InterfaceIndex, uint64(r.getUint32(r.buf[4:8]))<<32|uint64(r.getUint32(r.buf[8:12])))).UTC()
|
||||
r.ci.CaptureLength = int(r.getUint32(r.buf[12:16]))
|
||||
r.ci.Length = int(r.getUint32(r.buf[16:20]))
|
||||
break FIND_PACKET
|
||||
default:
|
||||
if _, err := r.r.Discard(int(r.currentBlock.length)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
if !r.options.WantMixedLinkType {
|
||||
if r.ifaces[r.ci.InterfaceIndex].LinkType != r.linkType {
|
||||
if _, err := r.r.Discard(int(r.currentBlock.length)); err != nil {
|
||||
return err
|
||||
}
|
||||
if r.options.ErrorOnMismatchingLinkType {
|
||||
return ErrNgLinkTypeMismatch
|
||||
}
|
||||
goto RESTART
|
||||
}
|
||||
return nil
|
||||
}
|
||||
r.ancil[0] = r.ifaces[r.ci.InterfaceIndex].LinkType
|
||||
return nil
|
||||
}
|
||||
|
||||
// ReadPacketData returns the next packet available from this data source.
|
||||
// If WantMixedLinkType is true, ci.AncillaryData[0] contains the link type.
|
||||
func (r *NgReader) ReadPacketData() (data []byte, ci gopacket.CaptureInfo, err error) {
|
||||
if err = r.readPacketHeader(); err != nil {
|
||||
return
|
||||
}
|
||||
ci = r.ci
|
||||
if r.options.WantMixedLinkType {
|
||||
ci.AncillaryData = make([]interface{}, 1)
|
||||
ci.AncillaryData[0] = r.ancil[0]
|
||||
}
|
||||
data = make([]byte, r.ci.CaptureLength)
|
||||
if err = r.readBytes(data); err != nil {
|
||||
return
|
||||
}
|
||||
// handle options somehow - this would be expensive
|
||||
_, err = r.r.Discard(int(r.currentBlock.length) - r.ci.CaptureLength)
|
||||
return
|
||||
}
|
||||
|
||||
// ZeroCopyReadPacketData returns the next packet available from this data source.
|
||||
// If WantMixedLinkType is true, ci.AncillaryData[0] contains the link type.
|
||||
// Warning: Like data, ci.AncillaryData is also reused and overwritten on the next call to ZeroCopyReadPacketData.
|
||||
//
|
||||
// It is not true zero copy, as data is still copied from the underlying reader. However,
|
||||
// this method avoids allocating heap memory for every packet.
|
||||
func (r *NgReader) ZeroCopyReadPacketData() (data []byte, ci gopacket.CaptureInfo, err error) {
|
||||
if err = r.readPacketHeader(); err != nil {
|
||||
return
|
||||
}
|
||||
ci = r.ci
|
||||
if r.options.WantMixedLinkType {
|
||||
ci.AncillaryData = r.ancil[:]
|
||||
}
|
||||
if cap(r.packetBuf) < ci.CaptureLength {
|
||||
snaplen := int(r.ifaces[ci.InterfaceIndex].SnapLength)
|
||||
if snaplen < ci.CaptureLength {
|
||||
snaplen = ci.CaptureLength
|
||||
}
|
||||
r.packetBuf = make([]byte, snaplen)
|
||||
}
|
||||
data = r.packetBuf[:ci.CaptureLength]
|
||||
if err = r.readBytes(data); err != nil {
|
||||
return
|
||||
}
|
||||
// handle options somehow - this would be expensive
|
||||
_, err = r.r.Discard(int(r.currentBlock.length) - ci.CaptureLength)
|
||||
return
|
||||
}
|
||||
|
||||
// LinkType returns the link type of the first interface, as a layers.LinkType. This is only valid, if WantMixedLinkType is false.
|
||||
func (r *NgReader) LinkType() layers.LinkType {
|
||||
return r.linkType
|
||||
}
|
||||
|
||||
// SectionInfo returns information about the current section.
|
||||
func (r *NgReader) SectionInfo() NgSectionInfo {
|
||||
return r.sectionInfo
|
||||
}
|
||||
|
||||
// Interface returns interface information and statistics of interface with the given id.
|
||||
func (r *NgReader) Interface(i int) (NgInterface, error) {
|
||||
if i >= len(r.ifaces) || i < 0 {
|
||||
return NgInterface{}, fmt.Errorf("Interface %d invalid. There are only %d interfaces", i, len(r.ifaces))
|
||||
}
|
||||
return r.ifaces[i], nil
|
||||
}
|
||||
|
||||
// NInterfaces returns the current number of interfaces.
|
||||
func (r *NgReader) NInterfaces() int {
|
||||
return len(r.ifaces)
|
||||
}
|
||||
|
||||
// Resolution returns the timestamp resolution of acquired timestamps before scaling to NanosecondTimestampResolution.
|
||||
func (r *NgReader) Resolution() gopacket.TimestampResolution {
|
||||
if r.options.WantMixedLinkType {
|
||||
return gopacket.TimestampResolution{}
|
||||
}
|
||||
return r.ifaces[0].Resolution()
|
||||
}
|
397
trunk/3rdparty/srs-bench/vendor/github.com/google/gopacket/pcapgo/ngwrite.go
generated
vendored
Normal file
397
trunk/3rdparty/srs-bench/vendor/github.com/google/gopacket/pcapgo/ngwrite.go
generated
vendored
Normal file
|
@ -0,0 +1,397 @@
|
|||
// Copyright 2018 The GoPacket Authors. All rights reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the LICENSE file in the root of the source
|
||||
// tree.
|
||||
|
||||
package pcapgo
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"io"
|
||||
"runtime"
|
||||
"time"
|
||||
|
||||
"github.com/google/gopacket"
|
||||
"github.com/google/gopacket/layers"
|
||||
)
|
||||
|
||||
// NgWriterOptions holds options for creating a pcapng file
|
||||
type NgWriterOptions struct {
|
||||
// SectionInfo will be written to the section header
|
||||
SectionInfo NgSectionInfo
|
||||
}
|
||||
|
||||
// DefaultNgWriterOptions contain defaults for a pcapng writer used by NewWriter
|
||||
var DefaultNgWriterOptions = NgWriterOptions{
|
||||
SectionInfo: NgSectionInfo{
|
||||
Hardware: runtime.GOARCH,
|
||||
OS: runtime.GOOS,
|
||||
Application: "gopacket", //spread the word
|
||||
},
|
||||
}
|
||||
|
||||
// DefaultNgInterface contains default interface options used by NewWriter
|
||||
var DefaultNgInterface = NgInterface{
|
||||
Name: "intf0",
|
||||
OS: runtime.GOOS,
|
||||
SnapLength: 0, //unlimited
|
||||
TimestampResolution: 9,
|
||||
}
|
||||
|
||||
// NgWriter holds the internal state of a pcapng file writer. Internally a bufio.NgWriter is used, therefore Flush must be called before closing the underlying file.
|
||||
type NgWriter struct {
|
||||
w *bufio.Writer
|
||||
options NgWriterOptions
|
||||
intf uint32
|
||||
buf [28]byte
|
||||
}
|
||||
|
||||
// NewNgWriter initializes and returns a new writer. Additionally, one section and one interface (without statistics) is written to the file. Interface and section options are used from DefaultNgInterface and DefaultNgWriterOptions.
|
||||
// Flush must be called before the file is closed, or if eventual unwritten information should be written out to the storage device.
|
||||
//
|
||||
// Written files are in little endian format. Interface timestamp resolution is fixed to 9 (to match time.Time).
|
||||
func NewNgWriter(w io.Writer, linkType layers.LinkType) (*NgWriter, error) {
|
||||
intf := DefaultNgInterface
|
||||
intf.LinkType = linkType
|
||||
return NewNgWriterInterface(w, intf, DefaultNgWriterOptions)
|
||||
}
|
||||
|
||||
// NewNgWriterInterface initializes and returns a new writer. Additionally, one section and one interface (without statistics) is written to the file.
|
||||
// Flush must be called before the file is closed, or if eventual unwritten information should be written out to the storage device.
|
||||
//
|
||||
// Written files are in little endian format. Interface timestamp resolution is fixed to 9 (to match time.Time).
|
||||
func NewNgWriterInterface(w io.Writer, intf NgInterface, options NgWriterOptions) (*NgWriter, error) {
|
||||
ret := &NgWriter{
|
||||
w: bufio.NewWriter(w),
|
||||
options: options,
|
||||
}
|
||||
if err := ret.writeSectionHeader(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if _, err := ret.AddInterface(intf); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
// ngOptionLength returns the needed length for one option value (without padding)
|
||||
func ngOptionLength(option ngOption) int {
|
||||
switch val := option.raw.(type) {
|
||||
case []byte:
|
||||
return len(val)
|
||||
case string:
|
||||
return len(val)
|
||||
case time.Time:
|
||||
return 8
|
||||
case uint64:
|
||||
return 8
|
||||
case uint32:
|
||||
return 4
|
||||
case uint8:
|
||||
return 1
|
||||
default:
|
||||
panic("This should never happen")
|
||||
}
|
||||
}
|
||||
|
||||
// prepareNgOptions fills out the length value of the given options and returns the number of octets needed for all the given options including padding.
|
||||
func prepareNgOptions(options []ngOption) uint32 {
|
||||
var ret uint32
|
||||
for i, option := range options {
|
||||
length := ngOptionLength(option)
|
||||
options[i].length = uint16(length)
|
||||
length += (4-length&3)&3 + // padding
|
||||
4 //header
|
||||
ret += uint32(length)
|
||||
}
|
||||
if ret > 0 {
|
||||
ret += 4 // end of options
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
// writeOptions writes the given options to the file. prepareOptions must be called beforehand.
|
||||
func (w *NgWriter) writeOptions(options []ngOption) error {
|
||||
if len(options) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
var zero [4]byte
|
||||
for _, option := range options {
|
||||
binary.LittleEndian.PutUint16(w.buf[0:2], uint16(option.code))
|
||||
binary.LittleEndian.PutUint16(w.buf[2:4], option.length)
|
||||
if _, err := w.w.Write(w.buf[:4]); err != nil {
|
||||
return err
|
||||
}
|
||||
switch val := option.raw.(type) {
|
||||
case []byte:
|
||||
if _, err := w.w.Write(val); err != nil {
|
||||
return err
|
||||
}
|
||||
padding := uint8((4 - option.length&3) & 3)
|
||||
if padding < 4 {
|
||||
if _, err := w.w.Write(zero[:padding]); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
case string:
|
||||
if _, err := w.w.Write([]byte(val)); err != nil {
|
||||
return err
|
||||
}
|
||||
padding := uint8((4 - option.length&3) & 3)
|
||||
if padding < 4 {
|
||||
if _, err := w.w.Write(zero[:padding]); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
case time.Time:
|
||||
ts := val.UnixNano()
|
||||
binary.LittleEndian.PutUint32(w.buf[:4], uint32(ts>>32))
|
||||
binary.LittleEndian.PutUint32(w.buf[4:8], uint32(ts))
|
||||
if _, err := w.w.Write(w.buf[:8]); err != nil {
|
||||
return err
|
||||
}
|
||||
case uint64:
|
||||
binary.LittleEndian.PutUint64(w.buf[:8], val)
|
||||
if _, err := w.w.Write(w.buf[:8]); err != nil {
|
||||
return err
|
||||
}
|
||||
case uint32:
|
||||
binary.LittleEndian.PutUint32(w.buf[:4], val)
|
||||
if _, err := w.w.Write(w.buf[:4]); err != nil {
|
||||
return err
|
||||
}
|
||||
case uint8:
|
||||
binary.LittleEndian.PutUint32(w.buf[:4], 0) // padding
|
||||
w.buf[0] = val
|
||||
if _, err := w.w.Write(w.buf[:4]); err != nil {
|
||||
return err
|
||||
}
|
||||
default:
|
||||
panic("This should never happen")
|
||||
}
|
||||
}
|
||||
|
||||
// options must be folled by an end of options option
|
||||
binary.LittleEndian.PutUint16(w.buf[0:2], uint16(ngOptionCodeEndOfOptions))
|
||||
binary.LittleEndian.PutUint16(w.buf[2:4], 0)
|
||||
_, err := w.w.Write(w.buf[:4])
|
||||
return err
|
||||
}
|
||||
|
||||
// writeSectionHeader writes a section header to the file
|
||||
func (w *NgWriter) writeSectionHeader() error {
|
||||
var scratch [4]ngOption
|
||||
i := 0
|
||||
info := w.options.SectionInfo
|
||||
if info.Application != "" {
|
||||
scratch[i].code = ngOptionCodeUserApplication
|
||||
scratch[i].raw = info.Application
|
||||
i++
|
||||
}
|
||||
if info.Comment != "" {
|
||||
scratch[i].code = ngOptionCodeComment
|
||||
scratch[i].raw = info.Comment
|
||||
i++
|
||||
}
|
||||
if info.Hardware != "" {
|
||||
scratch[i].code = ngOptionCodeHardware
|
||||
scratch[i].raw = info.Hardware
|
||||
i++
|
||||
}
|
||||
if info.OS != "" {
|
||||
scratch[i].code = ngOptionCodeOS
|
||||
scratch[i].raw = info.OS
|
||||
i++
|
||||
}
|
||||
options := scratch[:i]
|
||||
|
||||
length := prepareNgOptions(options) +
|
||||
24 + // header
|
||||
4 // trailer
|
||||
|
||||
binary.LittleEndian.PutUint32(w.buf[:4], uint32(ngBlockTypeSectionHeader))
|
||||
binary.LittleEndian.PutUint32(w.buf[4:8], length)
|
||||
binary.LittleEndian.PutUint32(w.buf[8:12], ngByteOrderMagic)
|
||||
binary.LittleEndian.PutUint16(w.buf[12:14], ngVersionMajor)
|
||||
binary.LittleEndian.PutUint16(w.buf[14:16], ngVersionMinor)
|
||||
binary.LittleEndian.PutUint64(w.buf[16:24], 0xFFFFFFFFFFFFFFFF) // unspecified
|
||||
if _, err := w.w.Write(w.buf[:24]); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := w.writeOptions(options); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
binary.LittleEndian.PutUint32(w.buf[0:4], length)
|
||||
_, err := w.w.Write(w.buf[:4])
|
||||
return err
|
||||
}
|
||||
|
||||
// AddInterface adds the specified interface to the file, excluding statistics. Interface timestamp resolution is fixed to 9 (to match time.Time). Empty values are not written.
|
||||
func (w *NgWriter) AddInterface(intf NgInterface) (id int, err error) {
|
||||
id = int(w.intf)
|
||||
w.intf++
|
||||
|
||||
var scratch [7]ngOption
|
||||
i := 0
|
||||
if intf.Name != "" {
|
||||
scratch[i].code = ngOptionCodeInterfaceName
|
||||
scratch[i].raw = intf.Name
|
||||
i++
|
||||
}
|
||||
if intf.Comment != "" {
|
||||
scratch[i].code = ngOptionCodeComment
|
||||
scratch[i].raw = intf.Comment
|
||||
i++
|
||||
}
|
||||
if intf.Description != "" {
|
||||
scratch[i].code = ngOptionCodeInterfaceDescription
|
||||
scratch[i].raw = intf.Description
|
||||
i++
|
||||
}
|
||||
if intf.Filter != "" {
|
||||
scratch[i].code = ngOptionCodeInterfaceFilter
|
||||
scratch[i].raw = append([]byte{0}, []byte(intf.Filter)...)
|
||||
i++
|
||||
}
|
||||
if intf.OS != "" {
|
||||
scratch[i].code = ngOptionCodeInterfaceOS
|
||||
scratch[i].raw = intf.OS
|
||||
i++
|
||||
}
|
||||
if intf.TimestampOffset != 0 {
|
||||
scratch[i].code = ngOptionCodeInterfaceTimestampOffset
|
||||
scratch[i].raw = intf.TimestampOffset
|
||||
i++
|
||||
}
|
||||
scratch[i].code = ngOptionCodeInterfaceTimestampResolution
|
||||
scratch[i].raw = uint8(9) // fix resolution to nanoseconds (time.Time) in decimal
|
||||
i++
|
||||
options := scratch[:i]
|
||||
|
||||
length := prepareNgOptions(options) +
|
||||
16 + // header
|
||||
4 // trailer
|
||||
|
||||
binary.LittleEndian.PutUint32(w.buf[:4], uint32(ngBlockTypeInterfaceDescriptor))
|
||||
binary.LittleEndian.PutUint32(w.buf[4:8], length)
|
||||
binary.LittleEndian.PutUint16(w.buf[8:10], uint16(intf.LinkType))
|
||||
binary.LittleEndian.PutUint16(w.buf[10:12], 0) // reserved value
|
||||
binary.LittleEndian.PutUint32(w.buf[12:16], intf.SnapLength)
|
||||
if _, err := w.w.Write(w.buf[:16]); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
if err := w.writeOptions(options); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
binary.LittleEndian.PutUint32(w.buf[0:4], length)
|
||||
_, err = w.w.Write(w.buf[:4])
|
||||
return id, err
|
||||
}
|
||||
|
||||
// WriteInterfaceStats writes the given interface statistics for the given interface id to the file. Empty values are not written.
|
||||
func (w *NgWriter) WriteInterfaceStats(intf int, stats NgInterfaceStatistics) error {
|
||||
if intf >= int(w.intf) || intf < 0 {
|
||||
return fmt.Errorf("Can't send statistics for non existent interface %d; have only %d interfaces", intf, w.intf)
|
||||
}
|
||||
|
||||
var scratch [4]ngOption
|
||||
i := 0
|
||||
if !stats.StartTime.IsZero() {
|
||||
scratch[i].code = ngOptionCodeInterfaceStatisticsStartTime
|
||||
scratch[i].raw = stats.StartTime
|
||||
i++
|
||||
}
|
||||
if !stats.EndTime.IsZero() {
|
||||
scratch[i].code = ngOptionCodeInterfaceStatisticsEndTime
|
||||
scratch[i].raw = stats.EndTime
|
||||
i++
|
||||
}
|
||||
if stats.PacketsDropped != NgNoValue64 {
|
||||
scratch[i].code = ngOptionCodeInterfaceStatisticsInterfaceDropped
|
||||
scratch[i].raw = stats.PacketsDropped
|
||||
i++
|
||||
}
|
||||
if stats.PacketsReceived != NgNoValue64 {
|
||||
scratch[i].code = ngOptionCodeInterfaceStatisticsInterfaceReceived
|
||||
scratch[i].raw = stats.PacketsReceived
|
||||
i++
|
||||
}
|
||||
options := scratch[:i]
|
||||
|
||||
length := prepareNgOptions(options) + 24
|
||||
|
||||
ts := stats.LastUpdate.UnixNano()
|
||||
if stats.LastUpdate.IsZero() {
|
||||
ts = 0
|
||||
}
|
||||
|
||||
binary.LittleEndian.PutUint32(w.buf[:4], uint32(ngBlockTypeInterfaceStatistics))
|
||||
binary.LittleEndian.PutUint32(w.buf[4:8], length)
|
||||
binary.LittleEndian.PutUint32(w.buf[8:12], uint32(intf))
|
||||
binary.LittleEndian.PutUint32(w.buf[12:16], uint32(ts>>32))
|
||||
binary.LittleEndian.PutUint32(w.buf[16:20], uint32(ts))
|
||||
if _, err := w.w.Write(w.buf[:20]); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := w.writeOptions(options); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
binary.LittleEndian.PutUint32(w.buf[0:4], length)
|
||||
_, err := w.w.Write(w.buf[:4])
|
||||
return err
|
||||
}
|
||||
|
||||
// WritePacket writes out packet with the given data and capture info. The given InterfaceIndex must already be added to the file. InterfaceIndex 0 is automatically added by the NewWriter* methods.
|
||||
func (w *NgWriter) WritePacket(ci gopacket.CaptureInfo, data []byte) error {
|
||||
if ci.InterfaceIndex >= int(w.intf) || ci.InterfaceIndex < 0 {
|
||||
return fmt.Errorf("Can't send statistics for non existent interface %d; have only %d interfaces", ci.InterfaceIndex, w.intf)
|
||||
}
|
||||
if ci.CaptureLength != len(data) {
|
||||
return fmt.Errorf("capture length %d does not match data length %d", ci.CaptureLength, len(data))
|
||||
}
|
||||
if ci.CaptureLength > ci.Length {
|
||||
return fmt.Errorf("invalid capture info %+v: capture length > length", ci)
|
||||
}
|
||||
|
||||
length := uint32(len(data)) + 32
|
||||
padding := (4 - length&3) & 3
|
||||
length += padding
|
||||
|
||||
ts := ci.Timestamp.UnixNano()
|
||||
|
||||
binary.LittleEndian.PutUint32(w.buf[:4], uint32(ngBlockTypeEnhancedPacket))
|
||||
binary.LittleEndian.PutUint32(w.buf[4:8], length)
|
||||
binary.LittleEndian.PutUint32(w.buf[8:12], uint32(ci.InterfaceIndex))
|
||||
binary.LittleEndian.PutUint32(w.buf[12:16], uint32(ts>>32))
|
||||
binary.LittleEndian.PutUint32(w.buf[16:20], uint32(ts))
|
||||
binary.LittleEndian.PutUint32(w.buf[20:24], uint32(ci.CaptureLength))
|
||||
binary.LittleEndian.PutUint32(w.buf[24:28], uint32(ci.Length))
|
||||
|
||||
if _, err := w.w.Write(w.buf[:28]); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err := w.w.Write(data); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
binary.LittleEndian.PutUint32(w.buf[:4], 0)
|
||||
_, err := w.w.Write(w.buf[4-padding : 8]) // padding + length
|
||||
return err
|
||||
}
|
||||
|
||||
// Flush writes out buffered data to the storage media. Must be called before closing the underlying file.
|
||||
func (w *NgWriter) Flush() error {
|
||||
return w.w.Flush()
|
||||
}
|
187
trunk/3rdparty/srs-bench/vendor/github.com/google/gopacket/pcapgo/pcapng.go
generated
vendored
Normal file
187
trunk/3rdparty/srs-bench/vendor/github.com/google/gopacket/pcapgo/pcapng.go
generated
vendored
Normal file
|
@ -0,0 +1,187 @@
|
|||
// Copyright 2018 The GoPacket Authors. All rights reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the LICENSE file in the root of the source
|
||||
// tree.
|
||||
|
||||
package pcapgo
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"math"
|
||||
"time"
|
||||
|
||||
"github.com/google/gopacket"
|
||||
"github.com/google/gopacket/layers"
|
||||
)
|
||||
|
||||
// ErrNgVersionMismatch gets returned for unknown pcapng section versions. This can only happen if ReaderOptions.SkipUnknownVersion == false
|
||||
var ErrNgVersionMismatch = errors.New("Unknown pcapng Version in Section Header")
|
||||
|
||||
// ErrNgLinkTypeMismatch gets returned if the link type of an interface is not the same as the link type from the first interface. This can only happen if ReaderOptions.ErrorOnMismatchingLinkType == true && ReaderOptions.WantMixedLinkType == false
|
||||
var ErrNgLinkTypeMismatch = errors.New("Link type of current interface is different from first one")
|
||||
|
||||
const (
|
||||
ngByteOrderMagic = 0x1A2B3C4D
|
||||
|
||||
// We can handle only version 1.0
|
||||
ngVersionMajor = 1
|
||||
ngVersionMinor = 0
|
||||
)
|
||||
|
||||
type ngBlockType uint32
|
||||
|
||||
const (
|
||||
ngBlockTypeInterfaceDescriptor ngBlockType = 1 // Interface description block
|
||||
ngBlockTypePacket ngBlockType = 2 // Packet block (deprecated)
|
||||
ngBlockTypeSimplePacket ngBlockType = 3 // Simple packet block
|
||||
ngBlockTypeInterfaceStatistics ngBlockType = 5 // Interface statistics block
|
||||
ngBlockTypeEnhancedPacket ngBlockType = 6 // Enhanced packet block
|
||||
ngBlockTypeSectionHeader ngBlockType = 0x0A0D0D0A // Section header block (same in both endians)
|
||||
)
|
||||
|
||||
type ngOptionCode uint16
|
||||
|
||||
const (
|
||||
ngOptionCodeEndOfOptions ngOptionCode = iota // end of options. must be at the end of options in a block
|
||||
ngOptionCodeComment // comment
|
||||
ngOptionCodeHardware // description of the hardware
|
||||
ngOptionCodeOS // name of the operating system
|
||||
ngOptionCodeUserApplication // name of the application
|
||||
)
|
||||
|
||||
const (
|
||||
ngOptionCodeInterfaceName ngOptionCode = iota + 2 // interface name
|
||||
ngOptionCodeInterfaceDescription // interface description
|
||||
ngOptionCodeInterfaceIPV4Address // IPv4 network address and netmask for the interface
|
||||
ngOptionCodeInterfaceIPV6Address // IPv6 network address and prefix length for the interface
|
||||
ngOptionCodeInterfaceMACAddress // interface hardware MAC address
|
||||
ngOptionCodeInterfaceEUIAddress // interface hardware EUI address
|
||||
ngOptionCodeInterfaceSpeed // interface speed in bits/s
|
||||
ngOptionCodeInterfaceTimestampResolution // timestamp resolution
|
||||
ngOptionCodeInterfaceTimezone // time zone
|
||||
ngOptionCodeInterfaceFilter // capture filter
|
||||
ngOptionCodeInterfaceOS // operating system
|
||||
ngOptionCodeInterfaceFCSLength // length of the Frame Check Sequence in bits
|
||||
ngOptionCodeInterfaceTimestampOffset // offset (in seconds) that must be added to packet timestamp
|
||||
)
|
||||
|
||||
const (
|
||||
ngOptionCodeInterfaceStatisticsStartTime ngOptionCode = iota + 2 // Start of capture
|
||||
ngOptionCodeInterfaceStatisticsEndTime // End of capture
|
||||
ngOptionCodeInterfaceStatisticsInterfaceReceived // Packets received by physical interface
|
||||
ngOptionCodeInterfaceStatisticsInterfaceDropped // Packets dropped by physical interface
|
||||
ngOptionCodeInterfaceStatisticsFilterAccept // Packets accepted by filter
|
||||
ngOptionCodeInterfaceStatisticsOSDrop // Packets dropped by operating system
|
||||
ngOptionCodeInterfaceStatisticsDelivered // Packets delivered to user
|
||||
)
|
||||
|
||||
// ngOption is a pcapng option
|
||||
type ngOption struct {
|
||||
code ngOptionCode
|
||||
value []byte
|
||||
raw interface{}
|
||||
length uint16
|
||||
}
|
||||
|
||||
// ngBlock is a pcapng block header
|
||||
type ngBlock struct {
|
||||
typ ngBlockType
|
||||
length uint32 // remaining length of block
|
||||
}
|
||||
|
||||
// NgResolution represents a pcapng timestamp resolution
|
||||
type NgResolution uint8
|
||||
|
||||
// Binary returns true if the timestamp resolution is a negative power of two. Otherwise NgResolution is a negative power of 10.
|
||||
func (r NgResolution) Binary() bool {
|
||||
if r&0x80 == 0x80 {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Exponent returns the negative exponent of the resolution.
|
||||
func (r NgResolution) Exponent() uint8 {
|
||||
return uint8(r) & 0x7f
|
||||
}
|
||||
|
||||
// ToTimestampResolution converts an NgResolution to a gopaket.TimestampResolution
|
||||
func (r NgResolution) ToTimestampResolution() (ret gopacket.TimestampResolution) {
|
||||
if r.Binary() {
|
||||
ret.Base = 2
|
||||
} else {
|
||||
ret.Base = 10
|
||||
}
|
||||
ret.Exponent = -int(r.Exponent())
|
||||
return
|
||||
}
|
||||
|
||||
// NgNoValue64 is a placeholder for an empty numeric 64 bit value.
|
||||
const NgNoValue64 = math.MaxUint64
|
||||
|
||||
// NgInterfaceStatistics hold the statistic for an interface at a single point in time. These values are already supposed to be accumulated. Most pcapng files contain this information at the end of the file/section.
|
||||
type NgInterfaceStatistics struct {
|
||||
// LastUpdate is the last time the statistics were updated.
|
||||
LastUpdate time.Time
|
||||
// StartTime is the time packet capture started on this interface. This value might be zero if this option is missing.
|
||||
StartTime time.Time
|
||||
// EndTime is the time packet capture ended on this interface This value might be zero if this option is missing.
|
||||
EndTime time.Time
|
||||
// Comment can be an arbitrary comment. This value might be empty if this option is missing.
|
||||
Comment string
|
||||
// PacketsReceived are the number of received packets. This value might be NoValue64 if this option is missing.
|
||||
PacketsReceived uint64
|
||||
// PacketsReceived are the number of received packets. This value might be NoValue64 if this option is missing.
|
||||
PacketsDropped uint64
|
||||
}
|
||||
|
||||
var ngEmptyStatistics = NgInterfaceStatistics{
|
||||
PacketsReceived: NgNoValue64,
|
||||
PacketsDropped: NgNoValue64,
|
||||
}
|
||||
|
||||
// NgInterface holds all the information of a pcapng interface.
|
||||
type NgInterface struct {
|
||||
// Name is the name of the interface. This value might be empty if this option is missing.
|
||||
Name string
|
||||
// Comment can be an arbitrary comment. This value might be empty if this option is missing.
|
||||
Comment string
|
||||
// Description is a description of the interface. This value might be empty if this option is missing.
|
||||
Description string
|
||||
// Filter is the filter used during packet capture. This value might be empty if this option is missing.
|
||||
Filter string
|
||||
// OS is the operating system this interface was controlled by. This value might be empty if this option is missing.
|
||||
OS string
|
||||
// LinkType is the linktype of the interface.
|
||||
LinkType layers.LinkType
|
||||
// TimestampResolution is the timestamp resolution of the packets in the pcapng file belonging to this interface.
|
||||
TimestampResolution NgResolution
|
||||
// TimestampResolution is the timestamp offset in seconds of the packets in the pcapng file belonging to this interface.
|
||||
TimestampOffset uint64
|
||||
// SnapLength is the maximum packet length captured by this interface. 0 for unlimited
|
||||
SnapLength uint32
|
||||
// Statistics holds the interface statistics
|
||||
Statistics NgInterfaceStatistics
|
||||
|
||||
secondMask uint64
|
||||
scaleUp uint64
|
||||
scaleDown uint64
|
||||
}
|
||||
|
||||
// Resolution returns the timestamp resolution of acquired timestamps before scaling to NanosecondTimestampResolution.
|
||||
func (i NgInterface) Resolution() gopacket.TimestampResolution {
|
||||
return i.TimestampResolution.ToTimestampResolution()
|
||||
}
|
||||
|
||||
// NgSectionInfo contains additional information of a pcapng section
|
||||
type NgSectionInfo struct {
|
||||
// Hardware is the hardware this file was generated on. This value might be empty if this option is missing.
|
||||
Hardware string
|
||||
// OS is the operating system this file was generated on. This value might be empty if this option is missing.
|
||||
OS string
|
||||
// Application is the user space application this file was generated with. This value might be empty if this option is missing.
|
||||
Application string
|
||||
// Comment can be an arbitrary comment. This value might be empty if this option is missing.
|
||||
Comment string
|
||||
}
|
231
trunk/3rdparty/srs-bench/vendor/github.com/google/gopacket/pcapgo/read.go
generated
vendored
Normal file
231
trunk/3rdparty/srs-bench/vendor/github.com/google/gopacket/pcapgo/read.go
generated
vendored
Normal file
|
@ -0,0 +1,231 @@
|
|||
// Copyright 2014 Damjan Cvetko. All rights reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the LICENSE file in the root of the source
|
||||
// tree.
|
||||
|
||||
package pcapgo
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"time"
|
||||
|
||||
"bufio"
|
||||
"compress/gzip"
|
||||
|
||||
"github.com/google/gopacket"
|
||||
"github.com/google/gopacket/layers"
|
||||
)
|
||||
|
||||
// Reader wraps an underlying io.Reader to read packet data in PCAP
|
||||
// format. See http://wiki.wireshark.org/Development/LibpcapFileFormat
|
||||
// for information on the file format.
|
||||
//
|
||||
// We currenty read v2.4 file format with nanosecond and microsecdond
|
||||
// timestamp resolution in little-endian and big-endian encoding.
|
||||
//
|
||||
// If the PCAP data is gzip compressed it is transparently uncompressed
|
||||
// by wrapping the given io.Reader with a gzip.Reader.
|
||||
type Reader struct {
|
||||
r io.Reader
|
||||
byteOrder binary.ByteOrder
|
||||
nanoSecsFactor uint32
|
||||
versionMajor uint16
|
||||
versionMinor uint16
|
||||
// timezone
|
||||
// sigfigs
|
||||
snaplen uint32
|
||||
linkType layers.LinkType
|
||||
// reusable buffer
|
||||
buf [16]byte
|
||||
// buffer for ZeroCopyReadPacketData
|
||||
packetBuf []byte
|
||||
}
|
||||
|
||||
const magicNanoseconds = 0xA1B23C4D
|
||||
const magicMicrosecondsBigendian = 0xD4C3B2A1
|
||||
const magicNanosecondsBigendian = 0x4D3CB2A1
|
||||
|
||||
const magicGzip1 = 0x1f
|
||||
const magicGzip2 = 0x8b
|
||||
|
||||
// NewReader returns a new reader object, for reading packet data from
|
||||
// the given reader. The reader must be open and header data is
|
||||
// read from it at this point.
|
||||
// If the file format is not supported an error is returned
|
||||
//
|
||||
// // Create new reader:
|
||||
// f, _ := os.Open("/tmp/file.pcap")
|
||||
// defer f.Close()
|
||||
// r, err := NewReader(f)
|
||||
// data, ci, err := r.ReadPacketData()
|
||||
func NewReader(r io.Reader) (*Reader, error) {
|
||||
ret := Reader{r: r}
|
||||
if err := ret.readHeader(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &ret, nil
|
||||
}
|
||||
|
||||
func (r *Reader) readHeader() error {
|
||||
br := bufio.NewReader(r.r)
|
||||
gzipMagic, err := br.Peek(2)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if gzipMagic[0] == magicGzip1 && gzipMagic[1] == magicGzip2 {
|
||||
if r.r, err = gzip.NewReader(br); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
r.r = br
|
||||
}
|
||||
|
||||
buf := make([]byte, 24)
|
||||
if n, err := io.ReadFull(r.r, buf); err != nil {
|
||||
return err
|
||||
} else if n < 24 {
|
||||
return errors.New("Not enough data for read")
|
||||
}
|
||||
if magic := binary.LittleEndian.Uint32(buf[0:4]); magic == magicNanoseconds {
|
||||
r.byteOrder = binary.LittleEndian
|
||||
r.nanoSecsFactor = 1
|
||||
} else if magic == magicNanosecondsBigendian {
|
||||
r.byteOrder = binary.BigEndian
|
||||
r.nanoSecsFactor = 1
|
||||
} else if magic == magicMicroseconds {
|
||||
r.byteOrder = binary.LittleEndian
|
||||
r.nanoSecsFactor = 1000
|
||||
} else if magic == magicMicrosecondsBigendian {
|
||||
r.byteOrder = binary.BigEndian
|
||||
r.nanoSecsFactor = 1000
|
||||
} else {
|
||||
return fmt.Errorf("Unknown magic %x", magic)
|
||||
}
|
||||
if r.versionMajor = r.byteOrder.Uint16(buf[4:6]); r.versionMajor != versionMajor {
|
||||
return fmt.Errorf("Unknown major version %d", r.versionMajor)
|
||||
}
|
||||
if r.versionMinor = r.byteOrder.Uint16(buf[6:8]); r.versionMinor != versionMinor {
|
||||
return fmt.Errorf("Unknown minor version %d", r.versionMinor)
|
||||
}
|
||||
// ignore timezone 8:12 and sigfigs 12:16
|
||||
r.snaplen = r.byteOrder.Uint32(buf[16:20])
|
||||
r.linkType = layers.LinkType(r.byteOrder.Uint32(buf[20:24]))
|
||||
return nil
|
||||
}
|
||||
|
||||
// ReadPacketData reads next packet from file.
|
||||
func (r *Reader) ReadPacketData() (data []byte, ci gopacket.CaptureInfo, err error) {
|
||||
if ci, err = r.readPacketHeader(); err != nil {
|
||||
return
|
||||
}
|
||||
if ci.CaptureLength > int(r.snaplen) {
|
||||
err = fmt.Errorf("capture length exceeds snap length: %d > %d", ci.CaptureLength, r.snaplen)
|
||||
return
|
||||
}
|
||||
if ci.CaptureLength > ci.Length {
|
||||
err = fmt.Errorf("capture length exceeds original packet length: %d > %d", ci.CaptureLength, ci.Length)
|
||||
return
|
||||
}
|
||||
data = make([]byte, ci.CaptureLength)
|
||||
_, err = io.ReadFull(r.r, data)
|
||||
return data, ci, err
|
||||
}
|
||||
|
||||
// ZeroCopyReadPacketData reads next packet from file. The data buffer is owned by the Reader,
|
||||
// and each call to ZeroCopyReadPacketData invalidates data returned by the previous one.
|
||||
//
|
||||
// It is not true zero copy, as data is still copied from the underlying reader. However,
|
||||
// this method avoids allocating heap memory for every packet.
|
||||
func (r *Reader) ZeroCopyReadPacketData() (data []byte, ci gopacket.CaptureInfo, err error) {
|
||||
if ci, err = r.readPacketHeader(); err != nil {
|
||||
return
|
||||
}
|
||||
if ci.CaptureLength > int(r.snaplen) {
|
||||
err = fmt.Errorf("capture length exceeds snap length: %d > %d", ci.CaptureLength, r.snaplen)
|
||||
return
|
||||
}
|
||||
if ci.CaptureLength > ci.Length {
|
||||
err = fmt.Errorf("capture length exceeds original packet length: %d > %d", ci.CaptureLength, ci.Length)
|
||||
return
|
||||
}
|
||||
|
||||
if cap(r.packetBuf) < ci.CaptureLength {
|
||||
snaplen := int(r.snaplen)
|
||||
if snaplen < ci.CaptureLength {
|
||||
snaplen = ci.CaptureLength
|
||||
}
|
||||
r.packetBuf = make([]byte, snaplen)
|
||||
}
|
||||
data = r.packetBuf[:ci.CaptureLength]
|
||||
_, err = io.ReadFull(r.r, data)
|
||||
return data, ci, err
|
||||
}
|
||||
|
||||
func (r *Reader) readPacketHeader() (ci gopacket.CaptureInfo, err error) {
|
||||
if _, err = io.ReadFull(r.r, r.buf[:]); err != nil {
|
||||
return
|
||||
}
|
||||
ci.Timestamp = time.Unix(int64(r.byteOrder.Uint32(r.buf[0:4])), int64(r.byteOrder.Uint32(r.buf[4:8])*r.nanoSecsFactor)).UTC()
|
||||
ci.CaptureLength = int(r.byteOrder.Uint32(r.buf[8:12]))
|
||||
ci.Length = int(r.byteOrder.Uint32(r.buf[12:16]))
|
||||
return
|
||||
}
|
||||
|
||||
// LinkType returns network, as a layers.LinkType.
|
||||
func (r *Reader) LinkType() layers.LinkType {
|
||||
return r.linkType
|
||||
}
|
||||
|
||||
// Snaplen returns the snapshot length of the capture file.
|
||||
func (r *Reader) Snaplen() uint32 {
|
||||
return r.snaplen
|
||||
}
|
||||
|
||||
// SetSnaplen sets the snapshot length of the capture file.
|
||||
//
|
||||
// This is useful when a pcap file contains packets bigger than then snaplen.
|
||||
// Pcapgo will error when reading packets bigger than snaplen, then it dumps those
|
||||
// packets and reads the next 16 bytes, which are part of the "faulty" packet's payload, but pcapgo
|
||||
// thinks it's the next header, which is probably also faulty because it's not really a packet header.
|
||||
// This can lead to a lot of faulty reads.
|
||||
//
|
||||
// The SetSnaplen function can be used to set a bigger snaplen to prevent those read errors.
|
||||
//
|
||||
// This snaplen situation can happen when a pcap writer doesn't truncate packets to the snaplen size while writing packets to file.
|
||||
// E.g. In Python, dpkt.pcap.Writer sets snaplen by default to 1500 (https://dpkt.readthedocs.io/en/latest/api/api_auto.html#dpkt.pcap.Writer)
|
||||
// but doesn't enforce this when writing packets (https://dpkt.readthedocs.io/en/latest/_modules/dpkt/pcap.html#Writer.writepkt).
|
||||
// When reading, tools like tcpdump, tcpslice, mergecap and wireshark ignore the snaplen and use
|
||||
// their own defined snaplen.
|
||||
// E.g. When reading packets, tcpdump defines MAXIMUM_SNAPLEN (https://github.com/the-tcpdump-group/tcpdump/blob/6e80fcdbe9c41366df3fa244ffe4ac8cce2ab597/netdissect.h#L290)
|
||||
// and uses it (https://github.com/the-tcpdump-group/tcpdump/blob/66384fa15b04b47ad08c063d4728df3b9c1c0677/print.c#L343-L358).
|
||||
//
|
||||
// For further reading:
|
||||
// - https://github.com/the-tcpdump-group/tcpdump/issues/389
|
||||
// - https://bugs.wireshark.org/bugzilla/show_bug.cgi?id=8808
|
||||
// - https://www.wireshark.org/lists/wireshark-dev/201307/msg00061.html
|
||||
// - https://github.com/wireshark/wireshark/blob/bfd51199e707c1d5c28732be34b44a9ee8a91cd8/wiretap/pcap-common.c#L723-L742
|
||||
// - https://github.com/wireshark/wireshark/blob/f07fb6cdfc0904905627707b88450054e921f092/wiretap/libpcap.c#L592-L598
|
||||
// - https://github.com/wireshark/wireshark/blob/f07fb6cdfc0904905627707b88450054e921f092/wiretap/libpcap.c#L714-L727
|
||||
// - https://github.com/the-tcpdump-group/tcpdump/commit/d033c1bc381c76d13e4aface97a4f4ec8c3beca2
|
||||
// - https://github.com/the-tcpdump-group/tcpdump/blob/88e87cb2cb74c5f939792171379acd9e0efd8b9a/netdissect.h#L263-L290
|
||||
func (r *Reader) SetSnaplen(newSnaplen uint32) {
|
||||
r.snaplen = newSnaplen
|
||||
}
|
||||
|
||||
// Reader formater
|
||||
func (r *Reader) String() string {
|
||||
return fmt.Sprintf("PcapFile maj: %x min: %x snaplen: %d linktype: %s", r.versionMajor, r.versionMinor, r.snaplen, r.linkType)
|
||||
}
|
||||
|
||||
// Resolution returns the timestamp resolution of acquired timestamps before scaling to NanosecondTimestampResolution.
|
||||
func (r *Reader) Resolution() gopacket.TimestampResolution {
|
||||
if r.nanoSecsFactor == 1 {
|
||||
return gopacket.TimestampResolutionMicrosecond
|
||||
}
|
||||
return gopacket.TimestampResolutionNanosecond
|
||||
}
|
170
trunk/3rdparty/srs-bench/vendor/github.com/google/gopacket/pcapgo/snoop.go
generated
vendored
Normal file
170
trunk/3rdparty/srs-bench/vendor/github.com/google/gopacket/pcapgo/snoop.go
generated
vendored
Normal file
|
@ -0,0 +1,170 @@
|
|||
// Copyright 2019 The GoPacket Authors. All rights reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the LICENSE file in the root of the source
|
||||
// tree.
|
||||
|
||||
package pcapgo
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"time"
|
||||
|
||||
"github.com/google/gopacket"
|
||||
"github.com/google/gopacket/layers"
|
||||
)
|
||||
|
||||
const snoopMagic uint64 = 0x736e6f6f70000000 //8 byte in big endian
|
||||
const snoopVersion uint32 = 2
|
||||
const maxCaptureLen int = 4096
|
||||
|
||||
// Errors
|
||||
const unknownMagic = "Unknown Snoop Magic Bytes"
|
||||
const unknownVersion = "Unknown Snoop Format Version"
|
||||
const unkownLinkType = "Unknown Link Type"
|
||||
const originalLenExceeded = "Capture length exceeds original packet length"
|
||||
const captureLenExceeded = "Capture length exceeds max capture length"
|
||||
|
||||
type snoopHeader struct {
|
||||
Version uint32
|
||||
linkType uint32
|
||||
}
|
||||
|
||||
// SnoopReader wraps an underlying io.SnoopReader to read packet data in SNOOP
|
||||
// format. See https://tools.ietf.org/html/rfc1761
|
||||
// for information on the file format.
|
||||
// We currenty read v2 file format and convert microsecond to nanoseconds
|
||||
// byte order in big-endian encoding.
|
||||
type SnoopReader struct {
|
||||
r io.Reader
|
||||
header snoopHeader
|
||||
//reuseable
|
||||
pad int
|
||||
packetBuf []byte
|
||||
buf [24]byte
|
||||
}
|
||||
|
||||
var (
|
||||
layerTypes = map[uint32]layers.LinkType{
|
||||
0: layers.LinkTypeEthernet, // IEEE 802.3
|
||||
2: layers.LinkTypeTokenRing, // IEEE 802.5 Token Ring
|
||||
4: layers.LinkTypeEthernet, // Ethernet
|
||||
5: layers.LinkTypeC_HDLC, // HDLC
|
||||
8: layers.LinkTypeFDDI, // FDDI
|
||||
/*
|
||||
10 - 4294967295 Unassigned
|
||||
not supported:
|
||||
1 - IEEE 802.4 Token Bus
|
||||
3 - IEEE 802.6 Metro Net
|
||||
6 - Character Synchronous
|
||||
7 - IBM Channel-to-Channel
|
||||
9 - Other
|
||||
*/
|
||||
}
|
||||
)
|
||||
|
||||
// LinkType return the mapped gopacket LinkType
|
||||
func (r *SnoopReader) LinkType() (*layers.LinkType, error) {
|
||||
if _, ok := layerTypes[r.header.linkType]; ok {
|
||||
lt := layerTypes[r.header.linkType]
|
||||
return <, nil
|
||||
}
|
||||
return nil, fmt.Errorf("%s, Code:%d", unkownLinkType, r.header.linkType)
|
||||
|
||||
}
|
||||
|
||||
// NewSnoopReader returns a new SnoopReader object, for reading packet data from
|
||||
// the given SnoopReader. The SnoopReader must be open and header data is
|
||||
// read from it at this point.
|
||||
// If the file format is not supported an error is returned
|
||||
func NewSnoopReader(r io.Reader) (*SnoopReader, error) {
|
||||
ret := SnoopReader{r: r}
|
||||
|
||||
if err := ret.readHeader(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &ret, nil
|
||||
}
|
||||
|
||||
func (r *SnoopReader) readHeader() error {
|
||||
buf := make([]byte, 16)
|
||||
|
||||
if n, err := io.ReadFull(r.r, buf); err != nil {
|
||||
return err
|
||||
} else if n < 16 {
|
||||
return errors.New("Not enough data for read")
|
||||
}
|
||||
|
||||
if magic := binary.BigEndian.Uint64(buf[0:8]); magic != snoopMagic {
|
||||
return fmt.Errorf("%s: %x", unknownMagic, magic)
|
||||
}
|
||||
|
||||
if r.header.Version = binary.BigEndian.Uint32(buf[8:12]); r.header.Version != snoopVersion {
|
||||
return fmt.Errorf("%s: %d", unknownVersion, r.header.Version)
|
||||
}
|
||||
|
||||
if r.header.linkType = binary.BigEndian.Uint32(buf[12:16]); r.header.linkType > 10 {
|
||||
return fmt.Errorf("%s, Code:%d", unkownLinkType, r.header.linkType)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *SnoopReader) readPacketHeader() (ci gopacket.CaptureInfo, err error) {
|
||||
|
||||
if _, err = io.ReadFull(r.r, r.buf[:]); err != nil {
|
||||
return
|
||||
}
|
||||
// OriginalLength uint32 4
|
||||
// IncludedLength uint32 8
|
||||
// PacketRecordLength uint32 12
|
||||
// CumulativeDrops uint32 16
|
||||
// TimestampSeconds uint32 20
|
||||
// TimestampMicroseconds uint32 24
|
||||
|
||||
ci.Timestamp = time.Unix(int64(binary.BigEndian.Uint32(r.buf[16:20])), int64(binary.BigEndian.Uint32(r.buf[20:24])*1000)).UTC()
|
||||
ci.Length = int(binary.BigEndian.Uint32(r.buf[0:4]))
|
||||
ci.CaptureLength = int(binary.BigEndian.Uint32(r.buf[4:8]))
|
||||
r.pad = int(binary.BigEndian.Uint32(r.buf[8:12])) - (24 + ci.Length)
|
||||
|
||||
if ci.CaptureLength > ci.Length {
|
||||
err = errors.New(originalLenExceeded)
|
||||
return
|
||||
}
|
||||
|
||||
if ci.CaptureLength > maxCaptureLen {
|
||||
err = errors.New(captureLenExceeded)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// ReadPacketData reads next packet data.
|
||||
func (r *SnoopReader) ReadPacketData() (data []byte, ci gopacket.CaptureInfo, err error) {
|
||||
if ci, err = r.readPacketHeader(); err != nil {
|
||||
return
|
||||
}
|
||||
data = make([]byte, ci.CaptureLength+r.pad)
|
||||
_, err = io.ReadFull(r.r, data)
|
||||
return data[:ci.CaptureLength], ci, err
|
||||
|
||||
}
|
||||
|
||||
// ZeroCopyReadPacketData reads next packet data. The data buffer is owned by the SnoopReader,
|
||||
// and each call to ZeroCopyReadPacketData invalidates data returned by the previous one.
|
||||
//
|
||||
// It is not true zero copy, as data is still copied from the underlying SnoopReader. However,
|
||||
// this method avoids allocating heap memory for every packet.
|
||||
func (r *SnoopReader) ZeroCopyReadPacketData() (data []byte, ci gopacket.CaptureInfo, err error) {
|
||||
if ci, err = r.readPacketHeader(); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if cap(r.packetBuf) < ci.CaptureLength+r.pad {
|
||||
r.packetBuf = make([]byte, ci.CaptureLength+r.pad)
|
||||
}
|
||||
_, err = io.ReadFull(r.r, r.packetBuf[:ci.CaptureLength+r.pad])
|
||||
return r.packetBuf[:ci.CaptureLength], ci, err
|
||||
}
|
129
trunk/3rdparty/srs-bench/vendor/github.com/google/gopacket/pcapgo/write.go
generated
vendored
Normal file
129
trunk/3rdparty/srs-bench/vendor/github.com/google/gopacket/pcapgo/write.go
generated
vendored
Normal file
|
@ -0,0 +1,129 @@
|
|||
// Copyright 2012 Google, Inc. All rights reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the LICENSE file in the root of the source
|
||||
// tree.
|
||||
|
||||
package pcapgo
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"io"
|
||||
"time"
|
||||
|
||||
"github.com/google/gopacket"
|
||||
"github.com/google/gopacket/layers"
|
||||
)
|
||||
|
||||
// Writer wraps an underlying io.Writer to write packet data in PCAP
|
||||
// format. See http://wiki.wireshark.org/Development/LibpcapFileFormat
|
||||
// for information on the file format.
|
||||
//
|
||||
// For those that care, we currently write v2.4 files with nanosecond
|
||||
// or microsecond timestamp resolution and little-endian encoding.
|
||||
type Writer struct {
|
||||
w io.Writer
|
||||
tsScaler int
|
||||
// Moving this into the struct seems to save an allocation for each call to writePacketHeader
|
||||
buf [16]byte
|
||||
}
|
||||
|
||||
const magicMicroseconds = 0xA1B2C3D4
|
||||
const versionMajor = 2
|
||||
const versionMinor = 4
|
||||
|
||||
// NewWriterNanos returns a new writer object, for writing packet data out
|
||||
// to the given writer. If this is a new empty writer (as opposed to
|
||||
// an append), you must call WriteFileHeader before WritePacket. Packet
|
||||
// timestamps are written with nanosecond precision.
|
||||
//
|
||||
// // Write a new file:
|
||||
// f, _ := os.Create("/tmp/file.pcap")
|
||||
// w := pcapgo.NewWriterNanos(f)
|
||||
// w.WriteFileHeader(65536, layers.LinkTypeEthernet) // new file, must do this.
|
||||
// w.WritePacket(gopacket.CaptureInfo{...}, data1)
|
||||
// f.Close()
|
||||
// // Append to existing file (must have same snaplen and linktype)
|
||||
// f2, _ := os.OpenFile("/tmp/fileNano.pcap", os.O_APPEND, 0700)
|
||||
// w2 := pcapgo.NewWriter(f2)
|
||||
// // no need for file header, it's already written.
|
||||
// w2.WritePacket(gopacket.CaptureInfo{...}, data2)
|
||||
// f2.Close()
|
||||
func NewWriterNanos(w io.Writer) *Writer {
|
||||
return &Writer{w: w, tsScaler: nanosPerNano}
|
||||
}
|
||||
|
||||
// NewWriter returns a new writer object, for writing packet data out
|
||||
// to the given writer. If this is a new empty writer (as opposed to
|
||||
// an append), you must call WriteFileHeader before WritePacket.
|
||||
// Packet timestamps are written witn microsecond precision.
|
||||
//
|
||||
// // Write a new file:
|
||||
// f, _ := os.Create("/tmp/file.pcap")
|
||||
// w := pcapgo.NewWriter(f)
|
||||
// w.WriteFileHeader(65536, layers.LinkTypeEthernet) // new file, must do this.
|
||||
// w.WritePacket(gopacket.CaptureInfo{...}, data1)
|
||||
// f.Close()
|
||||
// // Append to existing file (must have same snaplen and linktype)
|
||||
// f2, _ := os.OpenFile("/tmp/file.pcap", os.O_APPEND, 0700)
|
||||
// w2 := pcapgo.NewWriter(f2)
|
||||
// // no need for file header, it's already written.
|
||||
// w2.WritePacket(gopacket.CaptureInfo{...}, data2)
|
||||
// f2.Close()
|
||||
func NewWriter(w io.Writer) *Writer {
|
||||
return &Writer{w: w, tsScaler: nanosPerMicro}
|
||||
}
|
||||
|
||||
// WriteFileHeader writes a file header out to the writer.
|
||||
// This must be called exactly once per output.
|
||||
func (w *Writer) WriteFileHeader(snaplen uint32, linktype layers.LinkType) error {
|
||||
var buf [24]byte
|
||||
if w.tsScaler == nanosPerMicro {
|
||||
binary.LittleEndian.PutUint32(buf[0:4], magicMicroseconds)
|
||||
} else {
|
||||
binary.LittleEndian.PutUint32(buf[0:4], magicNanoseconds)
|
||||
}
|
||||
binary.LittleEndian.PutUint16(buf[4:6], versionMajor)
|
||||
binary.LittleEndian.PutUint16(buf[6:8], versionMinor)
|
||||
// bytes 8:12 stay 0 (timezone = UTC)
|
||||
// bytes 12:16 stay 0 (sigfigs is always set to zero, according to
|
||||
// http://wiki.wireshark.org/Development/LibpcapFileFormat
|
||||
binary.LittleEndian.PutUint32(buf[16:20], snaplen)
|
||||
binary.LittleEndian.PutUint32(buf[20:24], uint32(linktype))
|
||||
_, err := w.w.Write(buf[:])
|
||||
return err
|
||||
}
|
||||
|
||||
const nanosPerMicro = 1000
|
||||
const nanosPerNano = 1
|
||||
|
||||
func (w *Writer) writePacketHeader(ci gopacket.CaptureInfo) error {
|
||||
t := ci.Timestamp
|
||||
if t.IsZero() {
|
||||
t = time.Now()
|
||||
}
|
||||
secs := t.Unix()
|
||||
usecs := t.Nanosecond() / w.tsScaler
|
||||
binary.LittleEndian.PutUint32(w.buf[0:4], uint32(secs))
|
||||
binary.LittleEndian.PutUint32(w.buf[4:8], uint32(usecs))
|
||||
binary.LittleEndian.PutUint32(w.buf[8:12], uint32(ci.CaptureLength))
|
||||
binary.LittleEndian.PutUint32(w.buf[12:16], uint32(ci.Length))
|
||||
_, err := w.w.Write(w.buf[:])
|
||||
return err
|
||||
}
|
||||
|
||||
// WritePacket writes the given packet data out to the file.
|
||||
func (w *Writer) WritePacket(ci gopacket.CaptureInfo, data []byte) error {
|
||||
if ci.CaptureLength != len(data) {
|
||||
return fmt.Errorf("capture length %d does not match data length %d", ci.CaptureLength, len(data))
|
||||
}
|
||||
if ci.CaptureLength > ci.Length {
|
||||
return fmt.Errorf("invalid capture info %+v: capture length > length", ci)
|
||||
}
|
||||
if err := w.writePacketHeader(ci); err != nil {
|
||||
return fmt.Errorf("error writing packet header: %v", err)
|
||||
}
|
||||
_, err := w.w.Write(data)
|
||||
return err
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue