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

     1  // Copyright 2018 The GoPacket Authors. 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  	"bufio"
    11  	"encoding/binary"
    12  	"errors"
    13  	"fmt"
    14  	"io"
    15  	"time"
    16  
    17  	"github.com/gopacket/gopacket"
    18  	"github.com/gopacket/gopacket/layers"
    19  )
    20  
    21  // NgReaderOptions holds options for reading a pcapng file
    22  type NgReaderOptions struct {
    23  	// 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.
    24  	// If true the link type of the packet is also exposed via ci.AncillaryData[0].
    25  	WantMixedLinkType bool
    26  	// ErrorOnMismatchingLinkType enables returning an error if a packet with a link type not matching the first interface is encountered and WantMixedLinkType == false.
    27  	// If false packets those packets are just silently ignored, which is the libpcap behaviour.
    28  	ErrorOnMismatchingLinkType bool
    29  	// SkipUnknownVersion enables automatically skipping sections with an unknown version, which is recommended by the pcapng standard. Otherwise ErrVersionMismatch is returned.
    30  	SkipUnknownVersion bool
    31  	// 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.
    32  	// This is a good way to read interface statistics.
    33  	SectionEndCallback func([]NgInterface, NgSectionInfo)
    34  	// StatisticsCallback is called when a interface statistics block is read. The interface id and the read statistics are provided.
    35  	StatisticsCallback func(int, NgInterfaceStatistics)
    36  }
    37  
    38  // DefaultNgReaderOptions provides sane defaults for a pcapng reader.
    39  var DefaultNgReaderOptions = NgReaderOptions{}
    40  
    41  // NgReader wraps an underlying bufio.NgReader to read packet data in pcapng.
    42  type NgReader struct {
    43  	r                 *bufio.Reader
    44  	options           NgReaderOptions
    45  	sectionInfo       NgSectionInfo
    46  	linkType          layers.LinkType
    47  	ifaces            []NgInterface
    48  	currentBlock      ngBlock
    49  	currentOption     ngOption
    50  	buf               [24]byte
    51  	packetBuf         []byte
    52  	ci                gopacket.CaptureInfo
    53  	ancil             [1]interface{}
    54  	blen              int
    55  	firstSectionFound bool
    56  	activeSection     bool
    57  	bigEndian         bool
    58  	decryptionSecrets []decryptionSecret
    59  }
    60  
    61  // NewNgReader initializes a new writer, reads the first section header, and if necessary according to the options the first interface.
    62  func NewNgReader(r io.Reader, options NgReaderOptions) (*NgReader, error) {
    63  	ret := &NgReader{
    64  		r: bufio.NewReader(r),
    65  		currentOption: ngOption{
    66  			value: make([]byte, 1024),
    67  		},
    68  		decryptionSecrets: make([]decryptionSecret, 0),
    69  		options:           options,
    70  	}
    71  
    72  	//pcapng _must_ start with a section header
    73  	if err := ret.readBlock(); err != nil {
    74  		return nil, err
    75  	}
    76  	if ret.currentBlock.typ != ngBlockTypeSectionHeader {
    77  		return nil, fmt.Errorf("Unknown magic %x", ret.currentBlock.typ)
    78  	}
    79  
    80  	if err := ret.readSectionHeader(); err != nil {
    81  		return nil, err
    82  	}
    83  
    84  	return ret, nil
    85  }
    86  
    87  // First a couple of helper functions to speed things up
    88  
    89  // 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
    90  // Additionally this removes the bounds check compared to io.ReadFull due to the use of uint
    91  func (r *NgReader) readBytes(buffer []byte) error {
    92  	n := uint(0)
    93  	for n < uint(len(buffer)) {
    94  		nn, err := r.r.Read(buffer[n:])
    95  		n += uint(nn)
    96  		if err != nil {
    97  			return err
    98  		}
    99  	}
   100  	return nil
   101  }
   102  
   103  // The following functions make the binary.* functions inlineable (except for getUint64, which is too big, but not in any hot path anyway)
   104  // 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
   105  func (r *NgReader) getUint16(buffer []byte) uint16 {
   106  	if r.bigEndian {
   107  		return binary.BigEndian.Uint16(buffer)
   108  	}
   109  	return binary.LittleEndian.Uint16(buffer)
   110  }
   111  
   112  func (r *NgReader) getUint32(buffer []byte) uint32 {
   113  	if r.bigEndian {
   114  		return binary.BigEndian.Uint32(buffer)
   115  	}
   116  	return binary.LittleEndian.Uint32(buffer)
   117  }
   118  
   119  func (r *NgReader) getUint64(buffer []byte) uint64 {
   120  	if r.bigEndian {
   121  		return binary.BigEndian.Uint64(buffer)
   122  	}
   123  	return binary.LittleEndian.Uint64(buffer)
   124  }
   125  
   126  // Now the pcapng implementation
   127  
   128  // readBlock reads a the blocktype and length from the file. If the type is a section header, endianess is also read.
   129  func (r *NgReader) readBlock() error {
   130  	if err := r.readBytes(r.buf[0:8]); err != nil {
   131  		return err
   132  	}
   133  	r.currentBlock.typ = ngBlockType(r.getUint32(r.buf[0:4]))
   134  	// The next part is a bit fucked up since a section header could change the endianess...
   135  	// So first read then length just into a buffer, check if its a section header and then do the endianess part...
   136  	if r.currentBlock.typ == ngBlockTypeSectionHeader {
   137  		if err := r.readBytes(r.buf[8:12]); err != nil {
   138  			return err
   139  		}
   140  		if binary.BigEndian.Uint32(r.buf[8:12]) == ngByteOrderMagic {
   141  			r.bigEndian = true
   142  		} else if binary.LittleEndian.Uint32(r.buf[8:12]) == ngByteOrderMagic {
   143  			r.bigEndian = false
   144  		} else {
   145  			return errors.New("Wrong byte order value in Section Header")
   146  		}
   147  		// Set length to remaining length (length - (type + lengthfield = 8) - 4 for byteOrderMagic)
   148  		r.currentBlock.length = r.getUint32(r.buf[4:8]) - 8 - 4
   149  		return nil
   150  	}
   151  	// Set length to remaining length (length - (type + lengthfield = 8)
   152  	r.currentBlock.length = r.getUint32(r.buf[4:8]) - 8
   153  	return nil
   154  }
   155  
   156  // 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.
   157  func (r *NgReader) readOption() error {
   158  	if r.currentBlock.length == 4 {
   159  		// no more options
   160  		r.currentOption.code = ngOptionCodeEndOfOptions
   161  		return nil
   162  	}
   163  	if err := r.readBytes(r.buf[:4]); err != nil {
   164  		return err
   165  	}
   166  	r.currentBlock.length -= 4
   167  	r.currentOption.code = ngOptionCode(r.getUint16(r.buf[:2]))
   168  	length := r.getUint16(r.buf[2:4])
   169  	if r.currentOption.code == ngOptionCodeEndOfOptions {
   170  		if length != 0 {
   171  			return errors.New("End of Options must be zero length")
   172  		}
   173  		return nil
   174  	}
   175  	if length != 0 {
   176  		if length < uint16(cap(r.currentOption.value)) {
   177  			r.currentOption.value = r.currentOption.value[:length]
   178  		} else {
   179  			r.currentOption.value = make([]byte, length)
   180  		}
   181  		if err := r.readBytes(r.currentOption.value); err != nil {
   182  			return err
   183  		}
   184  		//consume padding
   185  		padding := length % 4
   186  		if padding > 0 {
   187  			padding = 4 - padding
   188  			if _, err := r.r.Discard(int(padding)); err != nil {
   189  				return err
   190  			}
   191  		}
   192  		r.currentBlock.length -= uint32(length + padding)
   193  	}
   194  	return nil
   195  }
   196  
   197  // readSectionHeader parses the full section header and implements section skipping in case of version mismatch
   198  // if needed, the first interface is read
   199  func (r *NgReader) readSectionHeader() error {
   200  	if r.options.SectionEndCallback != nil && r.activeSection {
   201  		interfaces := make([]NgInterface, len(r.ifaces))
   202  		for i := range r.ifaces {
   203  			interfaces[i] = r.ifaces[i]
   204  		}
   205  		r.options.SectionEndCallback(interfaces, r.sectionInfo)
   206  	}
   207  	// clear the interfaces
   208  	r.ifaces = r.ifaces[:0]
   209  	r.activeSection = false
   210  
   211  RESTART:
   212  	// read major, minor, section length
   213  	if err := r.readBytes(r.buf[:12]); err != nil {
   214  		return err
   215  	}
   216  	r.currentBlock.length -= 12
   217  
   218  	vMajor := r.getUint16(r.buf[0:2])
   219  	vMinor := r.getUint16(r.buf[2:4])
   220  	if vMajor != ngVersionMajor || vMinor != ngVersionMinor {
   221  		if !r.options.SkipUnknownVersion {
   222  			// Well the standard actually says to skip unknown version section headers,
   223  			// but this would mean user would be kept in the dark about whats going on...
   224  			return ErrNgVersionMismatch
   225  		}
   226  		if _, err := r.r.Discard(int(r.currentBlock.length)); err != nil {
   227  			return err
   228  		}
   229  		if err := r.skipSection(); err != nil {
   230  			return err
   231  		}
   232  		goto RESTART
   233  	}
   234  
   235  	var section NgSectionInfo
   236  
   237  OPTIONS:
   238  	for {
   239  		if err := r.readOption(); err != nil {
   240  			return err
   241  		}
   242  		switch r.currentOption.code {
   243  		case ngOptionCodeEndOfOptions:
   244  			break OPTIONS
   245  		case ngOptionCodeComment:
   246  			section.Comment = string(r.currentOption.value)
   247  		case ngOptionCodeHardware:
   248  			section.Hardware = string(r.currentOption.value)
   249  		case ngOptionCodeOS:
   250  			section.OS = string(r.currentOption.value)
   251  		case ngOptionCodeUserApplication:
   252  			section.Application = string(r.currentOption.value)
   253  		}
   254  	}
   255  
   256  	if _, err := r.r.Discard(int(r.currentBlock.length)); err != nil {
   257  		return err
   258  	}
   259  	r.activeSection = true
   260  	r.sectionInfo = section
   261  
   262  	if !r.options.WantMixedLinkType {
   263  		// If we don't want mixed link type, we need the first interface to fill Reader.LinkType()
   264  		// This handles most of the pcapngs out there, since they start with an IDB
   265  		if err := r.firstInterface(); err != nil {
   266  			return err
   267  		}
   268  	}
   269  
   270  	return nil
   271  }
   272  
   273  // skipSection skips blocks until the next section
   274  func (r *NgReader) skipSection() error {
   275  	for {
   276  		if err := r.readBlock(); err != nil {
   277  			return err
   278  		}
   279  		if r.currentBlock.typ == ngBlockTypeSectionHeader {
   280  			return nil
   281  		}
   282  		if _, err := r.r.Discard(int(r.currentBlock.length)); err != nil {
   283  			return err
   284  		}
   285  	}
   286  }
   287  
   288  // SkipSection skips the contents of the rest of the current section and reads the next section header.
   289  func (r *NgReader) SkipSection() error {
   290  	if err := r.skipSection(); err != nil {
   291  		return err
   292  	}
   293  	return r.readSectionHeader()
   294  }
   295  
   296  // firstInterface reads the first interface from the section and panics if a packet is encountered.
   297  func (r *NgReader) firstInterface() error {
   298  	for {
   299  		if err := r.readBlock(); err != nil {
   300  			return err
   301  		}
   302  		switch r.currentBlock.typ {
   303  		case ngBlockTypeInterfaceDescriptor:
   304  			if err := r.readInterfaceDescriptor(); err != nil {
   305  				return err
   306  			}
   307  			if !r.firstSectionFound {
   308  				r.linkType = r.ifaces[0].LinkType
   309  				r.firstSectionFound = true
   310  			} else if r.linkType != r.ifaces[0].LinkType {
   311  				if r.options.ErrorOnMismatchingLinkType {
   312  					return ErrNgLinkTypeMismatch
   313  				}
   314  				continue
   315  			}
   316  			return nil
   317  		case ngBlockTypePacket, ngBlockTypeEnhancedPacket, ngBlockTypeSimplePacket, ngBlockTypeInterfaceStatistics:
   318  			return errors.New("A section must have an interface before a packet block")
   319  		case ngBlockTypeDecryptionSecrets:
   320  			if err := r.readDecryptionSecretsBlock(); err != nil {
   321  				return err
   322  			}
   323  		}
   324  		if _, err := r.r.Discard(int(r.currentBlock.length)); err != nil {
   325  			return err
   326  		}
   327  	}
   328  }
   329  
   330  // readInterfaceDescriptor parses an interface descriptor, prepares timing calculation, and adds the interface details to the current list
   331  func (r *NgReader) readInterfaceDescriptor() error {
   332  	if err := r.readBytes(r.buf[:8]); err != nil {
   333  		return err
   334  	}
   335  	r.currentBlock.length -= 8
   336  	var intf NgInterface
   337  	intf.LinkType = layers.LinkType(r.getUint16(r.buf[:2]))
   338  	intf.SnapLength = r.getUint32(r.buf[4:8])
   339  
   340  OPTIONS:
   341  	for {
   342  		if err := r.readOption(); err != nil {
   343  			return err
   344  		}
   345  		switch r.currentOption.code {
   346  		case ngOptionCodeEndOfOptions:
   347  			break OPTIONS
   348  		case ngOptionCodeInterfaceName:
   349  			intf.Name = string(r.currentOption.value)
   350  		case ngOptionCodeComment:
   351  			intf.Comment = string(r.currentOption.value)
   352  		case ngOptionCodeInterfaceDescription:
   353  			intf.Description = string(r.currentOption.value)
   354  		case ngOptionCodeInterfaceFilter:
   355  			// ignore filter type (first byte) since it is not specified
   356  			intf.Filter = string(r.currentOption.value[1:])
   357  		case ngOptionCodeInterfaceOS:
   358  			intf.OS = string(r.currentOption.value)
   359  		case ngOptionCodeInterfaceTimestampOffset:
   360  			intf.TimestampOffset = r.getUint64(r.currentOption.value[:8])
   361  		case ngOptionCodeInterfaceTimestampResolution:
   362  			intf.TimestampResolution = NgResolution(r.currentOption.value[0])
   363  		}
   364  	}
   365  	if _, err := r.r.Discard(int(r.currentBlock.length)); err != nil {
   366  		return err
   367  	}
   368  	if intf.TimestampResolution == 0 {
   369  		intf.TimestampResolution = 6
   370  	}
   371  
   372  	//parse options
   373  	if intf.TimestampResolution.Binary() {
   374  		//negative power of 2
   375  		intf.secondMask = 1 << intf.TimestampResolution.Exponent()
   376  	} else {
   377  		//negative power of 10
   378  		intf.secondMask = 1
   379  		for j := uint8(0); j < intf.TimestampResolution.Exponent(); j++ {
   380  			intf.secondMask *= 10
   381  		}
   382  	}
   383  	intf.scaleDown = 1
   384  	intf.scaleUp = 1
   385  	if intf.secondMask < 1e9 {
   386  		intf.scaleUp = 1e9 / intf.secondMask
   387  	} else {
   388  		intf.scaleDown = intf.secondMask / 1e9
   389  	}
   390  	r.ifaces = append(r.ifaces, intf)
   391  	return nil
   392  }
   393  
   394  // convertTime adds offset + shifts the given time value according to the given interface
   395  func (r *NgReader) convertTime(ifaceID int, ts uint64) (int64, int64) {
   396  	iface := r.ifaces[ifaceID]
   397  	return int64(ts/iface.secondMask + iface.TimestampOffset), int64(ts % iface.secondMask * iface.scaleUp / iface.scaleDown)
   398  }
   399  
   400  // readInterfaceStatistics updates the statistics of the given interface
   401  func (r *NgReader) readInterfaceStatistics() error {
   402  	if err := r.readBytes(r.buf[:12]); err != nil {
   403  		return err
   404  	}
   405  	r.currentBlock.length -= 12
   406  	ifaceID := int(r.getUint32(r.buf[:4]))
   407  	ts := uint64(r.getUint32(r.buf[4:8]))<<32 | uint64(r.getUint32(r.buf[8:12]))
   408  	if int(ifaceID) >= len(r.ifaces) {
   409  		return fmt.Errorf("Interface id %d not present in section (have only %d interfaces)", ifaceID, len(r.ifaces))
   410  	}
   411  	stats := &r.ifaces[ifaceID].Statistics
   412  	*stats = ngEmptyStatistics
   413  	stats.LastUpdate = time.Unix(r.convertTime(ifaceID, ts)).UTC()
   414  
   415  OPTIONS:
   416  	for {
   417  		if err := r.readOption(); err != nil {
   418  			return err
   419  		}
   420  		switch r.currentOption.code {
   421  		case ngOptionCodeEndOfOptions:
   422  			break OPTIONS
   423  		case ngOptionCodeComment:
   424  			stats.Comment = string(r.currentOption.value)
   425  		case ngOptionCodeInterfaceStatisticsStartTime:
   426  			ts = uint64(r.getUint32(r.currentOption.value[:4]))<<32 | uint64(r.getUint32(r.currentOption.value[4:8]))
   427  			stats.StartTime = time.Unix(r.convertTime(ifaceID, ts)).UTC()
   428  		case ngOptionCodeInterfaceStatisticsEndTime:
   429  			ts = uint64(r.getUint32(r.currentOption.value[:4]))<<32 | uint64(r.getUint32(r.currentOption.value[4:8]))
   430  			stats.EndTime = time.Unix(r.convertTime(ifaceID, ts)).UTC()
   431  		case ngOptionCodeInterfaceStatisticsInterfaceReceived:
   432  			stats.PacketsReceived = r.getUint64(r.currentOption.value[:8])
   433  		case ngOptionCodeInterfaceStatisticsInterfaceDropped:
   434  			stats.PacketsDropped = r.getUint64(r.currentOption.value[:8])
   435  		}
   436  	}
   437  	if _, err := r.r.Discard(int(r.currentBlock.length)); err != nil {
   438  		return err
   439  	}
   440  	if r.options.StatisticsCallback != nil {
   441  		r.options.StatisticsCallback(ifaceID, *stats)
   442  	}
   443  	return nil
   444  }
   445  
   446  // readPacketHeader looks for a packet (enhanced, simple, or packet) and parses the header.
   447  // If an interface descriptor, an interface statistics block, or a section header is encountered, those are handled accordingly.
   448  // All other block types are skipped. New block types must be added here.
   449  func (r *NgReader) readPacketHeader() error {
   450  RESTART:
   451  FIND_PACKET:
   452  	for {
   453  		if err := r.readBlock(); err != nil {
   454  			return err
   455  		}
   456  		switch r.currentBlock.typ {
   457  		case ngBlockTypeEnhancedPacket:
   458  			if err := r.readBytes(r.buf[:20]); err != nil {
   459  				return err
   460  			}
   461  			r.currentBlock.length -= 20
   462  			r.ci.InterfaceIndex = int(r.getUint32(r.buf[:4]))
   463  			if r.ci.InterfaceIndex >= len(r.ifaces) {
   464  				return fmt.Errorf("Interface id %d not present in section (have only %d interfaces)", r.ci.InterfaceIndex, len(r.ifaces))
   465  			}
   466  			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()
   467  			r.ci.CaptureLength = int(r.getUint32(r.buf[12:16]))
   468  			r.ci.Length = int(r.getUint32(r.buf[16:20]))
   469  			break FIND_PACKET
   470  		case ngBlockTypeSimplePacket:
   471  			if err := r.readBytes(r.buf[:4]); err != nil {
   472  				return err
   473  			}
   474  			r.currentBlock.length -= 4
   475  			r.ci.Timestamp = time.Time{}
   476  			r.ci.InterfaceIndex = 0
   477  			r.ci.Length = int(r.getUint32(r.buf[:4]))
   478  			r.ci.CaptureLength = r.ci.Length
   479  			if len(r.ifaces) == 0 {
   480  				return errors.New("At least one Interface is needed for a packet")
   481  			}
   482  			if r.ifaces[0].SnapLength != 0 && uint32(r.ci.CaptureLength) > r.ifaces[0].SnapLength {
   483  				r.ci.CaptureLength = int(r.ifaces[0].SnapLength)
   484  			}
   485  			break FIND_PACKET
   486  		case ngBlockTypeInterfaceDescriptor:
   487  			if err := r.readInterfaceDescriptor(); err != nil {
   488  				return err
   489  			}
   490  		case ngBlockTypeInterfaceStatistics:
   491  			if err := r.readInterfaceStatistics(); err != nil {
   492  				return err
   493  			}
   494  		case ngBlockTypeSectionHeader:
   495  			if err := r.readSectionHeader(); err != nil {
   496  				return err
   497  			}
   498  		case ngBlockTypePacket:
   499  			if err := r.readBytes(r.buf[:20]); err != nil {
   500  				return err
   501  			}
   502  			r.currentBlock.length -= 20
   503  			r.ci.InterfaceIndex = int(r.getUint16(r.buf[0:2]))
   504  			if r.ci.InterfaceIndex >= len(r.ifaces) {
   505  				return fmt.Errorf("Interface id %d not present in section (have only %d interfaces)", r.ci.InterfaceIndex, len(r.ifaces))
   506  			}
   507  			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()
   508  			r.ci.CaptureLength = int(r.getUint32(r.buf[12:16]))
   509  			r.ci.Length = int(r.getUint32(r.buf[16:20]))
   510  			break FIND_PACKET
   511  		default:
   512  			if _, err := r.r.Discard(int(r.currentBlock.length)); err != nil {
   513  				return err
   514  			}
   515  		}
   516  	}
   517  	if !r.options.WantMixedLinkType {
   518  		if r.ifaces[r.ci.InterfaceIndex].LinkType != r.linkType {
   519  			if _, err := r.r.Discard(int(r.currentBlock.length)); err != nil {
   520  				return err
   521  			}
   522  			if r.options.ErrorOnMismatchingLinkType {
   523  				return ErrNgLinkTypeMismatch
   524  			}
   525  			goto RESTART
   526  		}
   527  		return nil
   528  	}
   529  	r.ancil[0] = r.ifaces[r.ci.InterfaceIndex].LinkType
   530  	return nil
   531  }
   532  
   533  // ReadPacketData returns the next packet available from this data source.
   534  // If WantMixedLinkType is true, ci.AncillaryData[0] contains the link type.
   535  func (r *NgReader) ReadPacketData() (data []byte, ci gopacket.CaptureInfo, err error) {
   536  	if err = r.readPacketHeader(); err != nil {
   537  		return
   538  	}
   539  	ci = r.ci
   540  	if r.options.WantMixedLinkType {
   541  		ci.AncillaryData = make([]interface{}, 1)
   542  		ci.AncillaryData[0] = r.ancil[0]
   543  	}
   544  	data = make([]byte, r.ci.CaptureLength)
   545  	if err = r.readBytes(data); err != nil {
   546  		return
   547  	}
   548  	// handle options somehow - this would be expensive
   549  	_, err = r.r.Discard(int(r.currentBlock.length) - r.ci.CaptureLength)
   550  	return
   551  }
   552  
   553  // ZeroCopyReadPacketData returns the next packet available from this data source.
   554  // If WantMixedLinkType is true, ci.AncillaryData[0] contains the link type.
   555  // Warning: Like data, ci.AncillaryData is also reused and overwritten on the next call to ZeroCopyReadPacketData.
   556  //
   557  // It is not true zero copy, as data is still copied from the underlying reader. However,
   558  // this method avoids allocating heap memory for every packet.
   559  func (r *NgReader) ZeroCopyReadPacketData() (data []byte, ci gopacket.CaptureInfo, err error) {
   560  	if err = r.readPacketHeader(); err != nil {
   561  		return
   562  	}
   563  	ci = r.ci
   564  	if r.options.WantMixedLinkType {
   565  		ci.AncillaryData = r.ancil[:]
   566  	}
   567  	if cap(r.packetBuf) < ci.CaptureLength {
   568  		snaplen := int(r.ifaces[ci.InterfaceIndex].SnapLength)
   569  		if snaplen < ci.CaptureLength {
   570  			snaplen = ci.CaptureLength
   571  		}
   572  		r.packetBuf = make([]byte, snaplen)
   573  	}
   574  	data = r.packetBuf[:ci.CaptureLength]
   575  	if err = r.readBytes(data); err != nil {
   576  		return
   577  	}
   578  	// handle options somehow - this would be expensive
   579  	_, err = r.r.Discard(int(r.currentBlock.length) - ci.CaptureLength)
   580  	return
   581  }
   582  
   583  // LinkType returns the link type of the first interface, as a layers.LinkType. This is only valid, if WantMixedLinkType is false.
   584  func (r *NgReader) LinkType() layers.LinkType {
   585  	return r.linkType
   586  }
   587  
   588  // SectionInfo returns information about the current section.
   589  func (r *NgReader) SectionInfo() NgSectionInfo {
   590  	return r.sectionInfo
   591  }
   592  
   593  // Interface returns interface information and statistics of interface with the given id.
   594  func (r *NgReader) Interface(i int) (NgInterface, error) {
   595  	if i >= len(r.ifaces) || i < 0 {
   596  		return NgInterface{}, fmt.Errorf("Interface %d invalid. There are only %d interfaces", i, len(r.ifaces))
   597  	}
   598  	return r.ifaces[i], nil
   599  }
   600  
   601  // NInterfaces returns the current number of interfaces.
   602  func (r *NgReader) NInterfaces() int {
   603  	return len(r.ifaces)
   604  }
   605  
   606  // Resolution returns the timestamp resolution of acquired timestamps before scaling to NanosecondTimestampResolution.
   607  func (r *NgReader) Resolution() gopacket.TimestampResolution {
   608  	if r.options.WantMixedLinkType {
   609  		return gopacket.TimestampResolution{}
   610  	}
   611  	return r.ifaces[0].Resolution()
   612  }