1
0
mirror of https://github.com/bettercap/bettercap.git synced 2025-03-12 04:36:03 -07:00

fix: updating gopacket to v1.1.16 fixed a bug which made wifi.recon off to timeout

This commit is contained in:
evilsocket 2019-02-06 12:24:00 +01:00
parent 4c5a776f86
commit b450747f4e
No known key found for this signature in database
GPG Key ID: 1564D7F30393A456
41 changed files with 4693 additions and 819 deletions

4
Gopkg.lock generated

@ -108,7 +108,7 @@
version = "v1.0.0"
[[projects]]
digest = "1:65e00adfb37f64ce3c24f139bc73db70bfe45224ebaf88250eff4038e32ac9cf"
digest = "1:b23296076e13a960263285b98907623e5d45f12fc405b14da19c6afa2a113deb"
name = "github.com/google/gopacket"
packages = [
".",
@ -117,7 +117,7 @@
"pcapgo",
]
pruneopts = "UT"
revision = "d67ddb98d5a1b7c79a8977ec2d552e1db45eda86"
revision = "v1.1.16"
[[projects]]
digest = "1:c79fb010be38a59d657c48c6ba1d003a8aa651fa56b579d959d74573b7dff8e1"

@ -57,7 +57,7 @@
[[constraint]]
name = "github.com/google/gopacket"
revision = "d67ddb98d5a1b7c79a8977ec2d552e1db45eda86"
revision = "v1.1.16"
[[constraint]]
name = "github.com/gorilla/mux"

@ -404,6 +404,7 @@ func (w *WiFiModule) Start() error {
w.updateStats(dot11, packet)
}
}
w.pktSourceChanClosed = true
})
@ -418,10 +419,8 @@ func (w *WiFiModule) Stop() error {
if !w.pktSourceChanClosed {
w.pktSourceChan <- nil
}
w.reads.Wait()
// close the pcap handle to make the main for exit
w.handle.Close()
// close the pcap handle to make the main for exit
// wait for the loop to exit.
w.reads.Wait()
})
}

@ -2,7 +2,7 @@
cd "$(dirname $0)"
go get github.com/golang/lint/golint
go get golang.org/x/lint/golint
DIRS=". tcpassembly tcpassembly/tcpreader ip4defrag reassembly macs pcapgo pcap afpacket pfring routing defrag/lcmdefrag"
# Add subdirectories here as we clean up golint on each.
for subdir in $DIRS; do

@ -14,6 +14,8 @@ Lukas Lueg <lukas.lueg@gmail.com>
Laurent Hausermann <laurent.hausermann@gmail.com>
Bill Green <bgreen@newrelic.com>
Christian Mäder <christian.maeder@nine.ch>
Gernot Vormayr <gvormayr@gmail.com>
Vitor Garcia Graveto <victor.graveto@gmail.com>
CONTRIBUTORS:
Attila Oláh <attila@attilaolah.eu>
@ -28,6 +30,7 @@ David Stainton <dstainton415@gmail.com>
Jesse Ward <jesse@jesseward.com>
Kane Mathers <kane@kanemathers.name>
Jose Selvi <jselvi@pentester.es>
Yerden Zhumabekov <yerden.zhumabekov@gmail.com>
-----------------------------------------------
FORKED FROM github.com/akrennmair/gopcap

@ -6,5 +6,7 @@ See [godoc](https://godoc.org/github.com/google/gopacket) for more details.
[![Build Status](https://travis-ci.org/google/gopacket.svg?branch=master)](https://travis-ci.org/google/gopacket)
[![GoDoc](https://godoc.org/github.com/google/gopacket?status.svg)](https://godoc.org/github.com/google/gopacket)
Minimum Go version required is 1.5.
Originally forked from the gopcap project written by Andreas
Krennmair <ak@synflood.at> (http://github.com/akrennmair/gopcap).

@ -22,6 +22,8 @@ useful, including:
Also, if you're looking to dive right into code, see the examples subdirectory
for numerous simple binaries built using gopacket libraries.
Minimum go version required is 1.5.
Basic Usage
gopacket takes in packet data as a []byte and decodes it into a packet with
@ -288,7 +290,10 @@ the packet's information. A quick example:
parser := gopacket.NewDecodingLayerParser(layers.LayerTypeEthernet, &eth, &ip4, &ip6, &tcp)
decoded := []gopacket.LayerType{}
for packetData := range somehowGetPacketData() {
err := parser.DecodeLayers(packetData, &decoded)
if err := parser.DecodeLayers(packetData, &decoded); err != nil {
fmt.Fprintf(os.Stderr, "Could not decode layers: %v\n", err)
continue
}
for _, layerType := range decoded {
switch layerType {
case layers.LayerTypeIPv6:

@ -7,7 +7,6 @@
package layers
import (
"bytes"
"encoding/binary"
"errors"
"fmt"
@ -158,7 +157,7 @@ func (d *DHCPv6) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.Serialize
if len(d.Options) > 0 {
for _, o := range d.Options {
if err := o.encode(data[offset:]); err != nil {
if err := o.encode(data[offset:], opts); err != nil {
return err
}
offset += int(o.Length) + 4 // 2 from option code, 2 from option length
@ -220,159 +219,6 @@ func (o DHCPv6StatusCode) String() string {
}
}
// DHCPv6Opt represents a DHCP option or parameter from RFC-3315
type DHCPv6Opt uint16
// Constants for the DHCPv6Opt options.
const (
DHCPv6OptClientID DHCPv6Opt = 1
DHCPv6OptServerID DHCPv6Opt = 2
DHCPv6OptIANA DHCPv6Opt = 3
DHCPv6OptIATA DHCPv6Opt = 4
DHCPv6OptIAAddr DHCPv6Opt = 5
DHCPv6OptOro DHCPv6Opt = 6
DHCPv6OptPreference DHCPv6Opt = 7
DHCPv6OptElapsedTime DHCPv6Opt = 8
DHCPv6OptRelayMessage DHCPv6Opt = 9
DHCPv6OptAuth DHCPv6Opt = 11
DHCPv6OptUnicast DHCPv6Opt = 12
DHCPv6OptStatusCode DHCPv6Opt = 13
DHCPv6OptRapidCommit DHCPv6Opt = 14
DHCPv6OptUserClass DHCPv6Opt = 15
DHCPv6OptVendorClass DHCPv6Opt = 16
DHCPv6OptVendorOpts DHCPv6Opt = 17
DHCPv6OptInterfaceID DHCPv6Opt = 18
DHCPv6OptReconfigureMessage DHCPv6Opt = 19
DHCPv6OptReconfigureAccept DHCPv6Opt = 20
)
// String returns a string version of a DHCPv6Opt.
func (o DHCPv6Opt) String() string {
switch o {
case DHCPv6OptClientID:
return "ClientID"
case DHCPv6OptServerID:
return "ServerID"
case DHCPv6OptIANA:
return "IA_NA"
case DHCPv6OptIATA:
return "IA_TA"
case DHCPv6OptIAAddr:
return "IAAddr"
case DHCPv6OptOro:
return "Oro"
case DHCPv6OptPreference:
return "Preference"
case DHCPv6OptElapsedTime:
return "ElapsedTime"
case DHCPv6OptRelayMessage:
return "RelayMessage"
case DHCPv6OptAuth:
return "Auth"
case DHCPv6OptUnicast:
return "Unicast"
case DHCPv6OptStatusCode:
return "StatusCode"
case DHCPv6OptRapidCommit:
return "RapidCommit"
case DHCPv6OptUserClass:
return "UserClass"
case DHCPv6OptVendorClass:
return "VendorClass"
case DHCPv6OptVendorOpts:
return "VendorOpts"
case DHCPv6OptInterfaceID:
return "InterfaceID"
case DHCPv6OptReconfigureMessage:
return "ReconfigureMessage"
case DHCPv6OptReconfigureAccept:
return "ReconfigureAccept"
default:
return "Unknown"
}
}
// DHCPv6Options is used to get nicely printed option lists which would normally
// be cut off after 5 options.
type DHCPv6Options []DHCPv6Option
// String returns a string version of the options list.
func (o DHCPv6Options) String() string {
buf := &bytes.Buffer{}
buf.WriteByte('[')
for i, opt := range o {
buf.WriteString(opt.String())
if i+1 != len(o) {
buf.WriteString(", ")
}
}
buf.WriteByte(']')
return buf.String()
}
// DHCPv6Option rerpresents a DHCP option.
type DHCPv6Option struct {
Code DHCPv6Opt
Length uint16
Data []byte
}
// String returns a string version of a DHCP Option.
func (o DHCPv6Option) String() string {
switch o.Code {
case DHCPv6OptClientID, DHCPv6OptServerID:
duid, err := decodeDHCPv6DUID(o.Data)
if err != nil {
return fmt.Sprintf("Option(%s:INVALID)", o.Code)
}
return fmt.Sprintf("Option(%s:[%s])", o.Code, duid.String())
case DHCPv6OptOro:
options := ""
for i := 0; i < int(o.Length); i += 2 {
if options != "" {
options += ","
}
option := DHCPv6Opt(binary.BigEndian.Uint16(o.Data[i : i+2]))
options += option.String()
}
return fmt.Sprintf("Option(%s:[%s])", o.Code, options)
default:
return fmt.Sprintf("Option(%s:%v)", o.Code, o.Data)
}
}
// NewDHCPv6Option constructs a new DHCPv6Option with a given type and data.
func NewDHCPv6Option(code DHCPv6Opt, data []byte) DHCPv6Option {
o := DHCPv6Option{Code: code}
if data != nil {
o.Data = data
o.Length = uint16(len(data))
}
return o
}
func (o *DHCPv6Option) encode(b []byte) error {
binary.BigEndian.PutUint16(b[0:2], uint16(o.Code))
binary.BigEndian.PutUint16(b[2:4], o.Length)
copy(b[4:], o.Data)
return nil
}
func (o *DHCPv6Option) decode(data []byte) error {
if len(data) < 2 {
return errors.New("Not enough data to decode")
}
o.Code = DHCPv6Opt(binary.BigEndian.Uint16(data[0:2]))
if len(data) < 3 {
return errors.New("Not enough data to decode")
}
o.Length = binary.BigEndian.Uint16(data[2:4])
o.Data = data[4 : 4+o.Length]
return nil
}
// DHCPv6DUIDType represents a DHCP DUID - RFC-3315
type DHCPv6DUIDType uint16

@ -0,0 +1,621 @@
// 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 layers
import (
"bytes"
"encoding/binary"
"errors"
"fmt"
"github.com/google/gopacket"
)
// DHCPv6Opt represents a DHCP option or parameter from RFC-3315
type DHCPv6Opt uint16
// Constants for the DHCPv6Opt options.
const (
DHCPv6OptClientID DHCPv6Opt = 1
DHCPv6OptServerID DHCPv6Opt = 2
DHCPv6OptIANA DHCPv6Opt = 3
DHCPv6OptIATA DHCPv6Opt = 4
DHCPv6OptIAAddr DHCPv6Opt = 5
DHCPv6OptOro DHCPv6Opt = 6
DHCPv6OptPreference DHCPv6Opt = 7
DHCPv6OptElapsedTime DHCPv6Opt = 8
DHCPv6OptRelayMessage DHCPv6Opt = 9
DHCPv6OptAuth DHCPv6Opt = 11
DHCPv6OptUnicast DHCPv6Opt = 12
DHCPv6OptStatusCode DHCPv6Opt = 13
DHCPv6OptRapidCommit DHCPv6Opt = 14
DHCPv6OptUserClass DHCPv6Opt = 15
DHCPv6OptVendorClass DHCPv6Opt = 16
DHCPv6OptVendorOpts DHCPv6Opt = 17
DHCPv6OptInterfaceID DHCPv6Opt = 18
DHCPv6OptReconfigureMessage DHCPv6Opt = 19
DHCPv6OptReconfigureAccept DHCPv6Opt = 20
// RFC 3319 Session Initiation Protocol (SIP)
DHCPv6OptSIPServersDomainList DHCPv6Opt = 21
DHCPv6OptSIPServersAddressList DHCPv6Opt = 22
// RFC 3646 DNS Configuration
DHCPv6OptDNSServers DHCPv6Opt = 23
DHCPv6OptDomainList DHCPv6Opt = 24
// RFC 3633 Prefix Delegation
DHCPv6OptIAPD DHCPv6Opt = 25
DHCPv6OptIAPrefix DHCPv6Opt = 26
// RFC 3898 Network Information Service (NIS)
DHCPv6OptNISServers DHCPv6Opt = 27
DHCPv6OptNISPServers DHCPv6Opt = 28
DHCPv6OptNISDomainName DHCPv6Opt = 29
DHCPv6OptNISPDomainName DHCPv6Opt = 30
// RFC 4075 Simple Network Time Protocol (SNTP)
DHCPv6OptSNTPServers DHCPv6Opt = 31
// RFC 4242 Information Refresh Time Option
DHCPv6OptInformationRefreshTime DHCPv6Opt = 32
// RFC 4280 Broadcast and Multicast Control Servers
DHCPv6OptBCMCSServerDomainNameList DHCPv6Opt = 33
DHCPv6OptBCMCSServerAddressList DHCPv6Opt = 34
// RFC 4776 Civic Address ConfigurationOption
DHCPv6OptGeoconfCivic DHCPv6Opt = 36
// RFC 4649 Relay Agent Remote-ID
DHCPv6OptRemoteID DHCPv6Opt = 37
// RFC 4580 Relay Agent Subscriber-ID
DHCPv6OptSubscriberID DHCPv6Opt = 38
// RFC 4704 Client Full Qualified Domain Name (FQDN)
DHCPv6OptClientFQDN DHCPv6Opt = 39
// RFC 5192 Protocol for Carrying Authentication for Network Access (PANA)
DHCPv6OptPanaAgent DHCPv6Opt = 40
// RFC 4833 Timezone Options
DHCPv6OptNewPOSIXTimezone DHCPv6Opt = 41
DHCPv6OptNewTZDBTimezone DHCPv6Opt = 42
// RFC 4994 Relay Agent Echo Request
DHCPv6OptEchoRequestOption DHCPv6Opt = 43
// RFC 5007 Leasequery
DHCPv6OptLQQuery DHCPv6Opt = 44
DHCPv6OptCLTTime DHCPv6Opt = 45
DHCPv6OptClientData DHCPv6Opt = 46
DHCPv6OptLQRelayData DHCPv6Opt = 47
DHCPv6OptLQClientLink DHCPv6Opt = 48
// RFC 6610 Home Information Discovery in Mobile IPv6 (MIPv6)
DHCPv6OptMIP6HNIDF DHCPv6Opt = 49
DHCPv6OptMIP6VDINF DHCPv6Opt = 50
DHCPv6OptMIP6IDINF DHCPv6Opt = 69
DHCPv6OptMIP6UDINF DHCPv6Opt = 70
DHCPv6OptMIP6HNP DHCPv6Opt = 71
DHCPv6OptMIP6HAA DHCPv6Opt = 72
DHCPv6OptMIP6HAF DHCPv6Opt = 73
// RFC 5223 Discovering Location-to-Service Translation (LoST) Servers
DHCPv6OptV6LOST DHCPv6Opt = 51
// RFC 5417 Control And Provisioning of Wireless Access Points (CAPWAP)
DHCPv6OptCAPWAPACV6 DHCPv6Opt = 52
// RFC 5460 Bulk Leasequery
DHCPv6OptRelayID DHCPv6Opt = 53
// RFC 5678 IEEE 802.21 Mobility Services (MoS) Discovery
DHCPv6OptIPv6AddressMoS DHCPv6Opt = 54
DHCPv6OptIPv6FQDNMoS DHCPv6Opt = 55
// RFC 5908 NTP Server Option
DHCPv6OptNTPServer DHCPv6Opt = 56
// RFC 5986 Discovering the Local Location Information Server (LIS)
DHCPv6OptV6AccessDomain DHCPv6Opt = 57
// RFC 5986 SIP User Agent
DHCPv6OptSIPUACSList DHCPv6Opt = 58
// RFC 5970 Options for Network Boot
DHCPv6OptBootFileURL DHCPv6Opt = 59
DHCPv6OptBootFileParam DHCPv6Opt = 60
DHCPv6OptClientArchType DHCPv6Opt = 61
DHCPv6OptNII DHCPv6Opt = 62
// RFC 6225 Coordinate-Based Location Configuration Information
DHCPv6OptGeolocation DHCPv6Opt = 63
// RFC 6334 Dual-Stack Lite
DHCPv6OptAFTRName DHCPv6Opt = 64
// RFC 6440 EAP Re-authentication Protocol (ERP)
DHCPv6OptERPLocalDomainName DHCPv6Opt = 65
// RFC 6422 Relay-Supplied DHCP Options
DHCPv6OptRSOO DHCPv6Opt = 66
// RFC 6603 Prefix Exclude Option for DHCPv6-based Prefix Delegation
DHCPv6OptPDExclude DHCPv6Opt = 67
// RFC 6607 Virtual Subnet Selection
DHCPv6OptVSS DHCPv6Opt = 68
// RFC 6731 Improved Recursive DNS Server Selection for Multi-Interfaced Nodes
DHCPv6OptRDNSSSelection DHCPv6Opt = 74
// RFC 6784 Kerberos Options for DHCPv6
DHCPv6OptKRBPrincipalName DHCPv6Opt = 75
DHCPv6OptKRBRealmName DHCPv6Opt = 76
DHCPv6OptKRBKDC DHCPv6Opt = 77
// RFC 6939 Client Link-Layer Address Option
DHCPv6OptClientLinkLayerAddress DHCPv6Opt = 79
// RFC 6977 Triggering DHCPv6 Reconfiguration from Relay Agents
DHCPv6OptLinkAddress DHCPv6Opt = 80
// RFC 7037 RADIUS Option for the DHCPv6 Relay Agent
DHCPv6OptRADIUS DHCPv6Opt = 81
// RFC 7083 Modification to Default Values of SOL_MAX_RT and INF_MAX_RT
DHCPv6OptSolMaxRt DHCPv6Opt = 82
DHCPv6OptInfMaxRt DHCPv6Opt = 83
// RFC 7078 Distributing Address Selection Policy
DHCPv6OptAddrSel DHCPv6Opt = 84
DHCPv6OptAddrSelTable DHCPv6Opt = 85
// RFC 7291 DHCP Options for the Port Control Protocol (PCP)
DHCPv6OptV6PCPServer DHCPv6Opt = 86
// RFC 7341 DHCPv4-over-DHCPv6 (DHCP 4o6) Transport
DHCPv6OptDHCPv4Message DHCPv6Opt = 87
DHCPv6OptDHCPv4OverDHCPv6Server DHCPv6Opt = 88
// RFC 7598 Configuration of Softwire Address and Port-Mapped Clients
DHCPv6OptS46Rule DHCPv6Opt = 89
DHCPv6OptS46BR DHCPv6Opt = 90
DHCPv6OptS46DMR DHCPv6Opt = 91
DHCPv6OptS46V4V4Bind DHCPv6Opt = 92
DHCPv6OptS46PortParameters DHCPv6Opt = 93
DHCPv6OptS46ContMAPE DHCPv6Opt = 94
DHCPv6OptS46ContMAPT DHCPv6Opt = 95
DHCPv6OptS46ContLW DHCPv6Opt = 96
// RFC 7600 IPv4 Residual Deployment via IPv6
DHCPv6Opt4RD DHCPv6Opt = 97
DHCPv6Opt4RDMapRule DHCPv6Opt = 98
DHCPv6Opt4RDNonMapRule DHCPv6Opt = 99
// RFC 7653 Active Leasequery
DHCPv6OptLQBaseTime DHCPv6Opt = 100
DHCPv6OptLQStartTime DHCPv6Opt = 101
DHCPv6OptLQEndTime DHCPv6Opt = 102
// RFC 7710 Captive-Portal Identification
DHCPv6OptCaptivePortal DHCPv6Opt = 103
// RFC 7774 Multicast Protocol for Low-Power and Lossy Networks (MPL) Parameter Configuration
DHCPv6OptMPLParameters DHCPv6Opt = 104
// RFC 7839 Access-Network-Identifier (ANI)
DHCPv6OptANIATT DHCPv6Opt = 105
DHCPv6OptANINetworkName DHCPv6Opt = 106
DHCPv6OptANIAPName DHCPv6Opt = 107
DHCPv6OptANIAPBSSID DHCPv6Opt = 108
DHCPv6OptANIOperatorID DHCPv6Opt = 109
DHCPv6OptANIOperatorRealm DHCPv6Opt = 110
// RFC 8026 Unified IPv4-in-IPv6 Softwire Customer Premises Equipment (CPE)
DHCPv6OptS46Priority DHCPv6Opt = 111
// draft-ietf-opsawg-mud-25 Manufacturer Usage Description (MUD)
DHCPv6OptMUDURLV6 DHCPv6Opt = 112
// RFC 8115 IPv4-Embedded Multicast and Unicast IPv6 Prefixes
DHCPv6OptV6Prefix64 DHCPv6Opt = 113
// RFC 8156 DHCPv6 Failover Protocol
DHCPv6OptFBindingStatus DHCPv6Opt = 114
DHCPv6OptFConnectFlags DHCPv6Opt = 115
DHCPv6OptFDNSRemovalInfo DHCPv6Opt = 116
DHCPv6OptFDNSHostName DHCPv6Opt = 117
DHCPv6OptFDNSZoneName DHCPv6Opt = 118
DHCPv6OptFDNSFlags DHCPv6Opt = 119
DHCPv6OptFExpirationTime DHCPv6Opt = 120
DHCPv6OptFMaxUnacknowledgedBNDUPD DHCPv6Opt = 121
DHCPv6OptFMCLT DHCPv6Opt = 122
DHCPv6OptFPartnerLifetime DHCPv6Opt = 123
DHCPv6OptFPartnerLifetimeSent DHCPv6Opt = 124
DHCPv6OptFPartnerDownTime DHCPv6Opt = 125
DHCPv6OptFPartnerRawCltTime DHCPv6Opt = 126
DHCPv6OptFProtocolVersion DHCPv6Opt = 127
DHCPv6OptFKeepaliveTime DHCPv6Opt = 128
DHCPv6OptFReconfigureData DHCPv6Opt = 129
DHCPv6OptFRelationshipName DHCPv6Opt = 130
DHCPv6OptFServerFlags DHCPv6Opt = 131
DHCPv6OptFServerState DHCPv6Opt = 132
DHCPv6OptFStartTimeOfState DHCPv6Opt = 133
DHCPv6OptFStateExpirationTime DHCPv6Opt = 134
// RFC 8357 Generalized UDP Source Port for DHCP Relay
DHCPv6OptRelayPort DHCPv6Opt = 135
// draft-ietf-netconf-zerotouch-25 Zero Touch Provisioning for Networking Devices
DHCPv6OptV6ZeroTouchRedirect DHCPv6Opt = 136
// RFC 6153 Access Network Discovery and Selection Function (ANDSF) Discovery
DHCPv6OptIPV6AddressANDSF DHCPv6Opt = 143
)
// String returns a string version of a DHCPv6Opt.
func (o DHCPv6Opt) String() string {
switch o {
case DHCPv6OptClientID:
return "ClientID"
case DHCPv6OptServerID:
return "ServerID"
case DHCPv6OptIANA:
return "IA_NA"
case DHCPv6OptIATA:
return "IA_TA"
case DHCPv6OptIAAddr:
return "IAAddr"
case DHCPv6OptOro:
return "Oro"
case DHCPv6OptPreference:
return "Preference"
case DHCPv6OptElapsedTime:
return "ElapsedTime"
case DHCPv6OptRelayMessage:
return "RelayMessage"
case DHCPv6OptAuth:
return "Auth"
case DHCPv6OptUnicast:
return "Unicast"
case DHCPv6OptStatusCode:
return "StatusCode"
case DHCPv6OptRapidCommit:
return "RapidCommit"
case DHCPv6OptUserClass:
return "UserClass"
case DHCPv6OptVendorClass:
return "VendorClass"
case DHCPv6OptVendorOpts:
return "VendorOpts"
case DHCPv6OptInterfaceID:
return "InterfaceID"
case DHCPv6OptReconfigureMessage:
return "ReconfigureMessage"
case DHCPv6OptReconfigureAccept:
return "ReconfigureAccept"
case DHCPv6OptSIPServersDomainList:
return "SIPServersDomainList"
case DHCPv6OptSIPServersAddressList:
return "SIPServersAddressList"
case DHCPv6OptDNSServers:
return "DNSRecursiveNameServer"
case DHCPv6OptDomainList:
return "DomainSearchList"
case DHCPv6OptIAPD:
return "IdentityAssociationPrefixDelegation"
case DHCPv6OptIAPrefix:
return "IAPDPrefix"
case DHCPv6OptNISServers:
return "NISServers"
case DHCPv6OptNISPServers:
return "NISv2Servers"
case DHCPv6OptNISDomainName:
return "NISDomainName"
case DHCPv6OptNISPDomainName:
return "NISv2DomainName"
case DHCPv6OptSNTPServers:
return "SNTPServers"
case DHCPv6OptInformationRefreshTime:
return "InformationRefreshTime"
case DHCPv6OptBCMCSServerDomainNameList:
return "BCMCSControlServersDomainNameList"
case DHCPv6OptBCMCSServerAddressList:
return "BCMCSControlServersAddressList"
case DHCPv6OptGeoconfCivic:
return "CivicAddress"
case DHCPv6OptRemoteID:
return "RelayAgentRemoteID"
case DHCPv6OptSubscriberID:
return "RelayAgentSubscriberID"
case DHCPv6OptClientFQDN:
return "ClientFQDN"
case DHCPv6OptPanaAgent:
return "PANAAuthenticationAgent"
case DHCPv6OptNewPOSIXTimezone:
return "NewPOSIXTimezone"
case DHCPv6OptNewTZDBTimezone:
return "NewTZDBTimezone"
case DHCPv6OptEchoRequestOption:
return "EchoRequest"
case DHCPv6OptLQQuery:
return "LeasequeryQuery"
case DHCPv6OptClientData:
return "LeasequeryClientData"
case DHCPv6OptCLTTime:
return "LeasequeryClientLastTransactionTime"
case DHCPv6OptLQRelayData:
return "LeasequeryRelayData"
case DHCPv6OptLQClientLink:
return "LeasequeryClientLink"
case DHCPv6OptMIP6HNIDF:
return "MIPv6HomeNetworkIDFQDN"
case DHCPv6OptMIP6VDINF:
return "MIPv6VisitedHomeNetworkInformation"
case DHCPv6OptMIP6IDINF:
return "MIPv6IdentifiedHomeNetworkInformation"
case DHCPv6OptMIP6UDINF:
return "MIPv6UnrestrictedHomeNetworkInformation"
case DHCPv6OptMIP6HNP:
return "MIPv6HomeNetworkPrefix"
case DHCPv6OptMIP6HAA:
return "MIPv6HomeAgentAddress"
case DHCPv6OptMIP6HAF:
return "MIPv6HomeAgentFQDN"
case DHCPv6OptV6LOST:
return "LoST Server"
case DHCPv6OptCAPWAPACV6:
return "CAPWAPAccessControllerV6"
case DHCPv6OptRelayID:
return "LeasequeryRelayID"
case DHCPv6OptIPv6AddressMoS:
return "MoSIPv6Address"
case DHCPv6OptIPv6FQDNMoS:
return "MoSDomainNameList"
case DHCPv6OptNTPServer:
return "NTPServer"
case DHCPv6OptV6AccessDomain:
return "AccessNetworkDomainName"
case DHCPv6OptSIPUACSList:
return "SIPUserAgentConfigurationServiceDomains"
case DHCPv6OptBootFileURL:
return "BootFileURL"
case DHCPv6OptBootFileParam:
return "BootFileParameters"
case DHCPv6OptClientArchType:
return "ClientSystemArchitectureType"
case DHCPv6OptNII:
return "ClientNetworkInterfaceIdentifier"
case DHCPv6OptGeolocation:
return "Geolocation"
case DHCPv6OptAFTRName:
return "AFTRName"
case DHCPv6OptERPLocalDomainName:
return "AFTRName"
case DHCPv6OptRSOO:
return "RSOOption"
case DHCPv6OptPDExclude:
return "PrefixExclude"
case DHCPv6OptVSS:
return "VirtualSubnetSelection"
case DHCPv6OptRDNSSSelection:
return "RDNSSSelection"
case DHCPv6OptKRBPrincipalName:
return "KerberosPrincipalName"
case DHCPv6OptKRBRealmName:
return "KerberosRealmName"
case DHCPv6OptKRBKDC:
return "KerberosKDC"
case DHCPv6OptClientLinkLayerAddress:
return "ClientLinkLayerAddress"
case DHCPv6OptLinkAddress:
return "LinkAddress"
case DHCPv6OptRADIUS:
return "RADIUS"
case DHCPv6OptSolMaxRt:
return "SolMaxRt"
case DHCPv6OptInfMaxRt:
return "InfMaxRt"
case DHCPv6OptAddrSel:
return "AddressSelection"
case DHCPv6OptAddrSelTable:
return "AddressSelectionTable"
case DHCPv6OptV6PCPServer:
return "PCPServer"
case DHCPv6OptDHCPv4Message:
return "DHCPv4Message"
case DHCPv6OptDHCPv4OverDHCPv6Server:
return "DHCP4o6ServerAddress"
case DHCPv6OptS46Rule:
return "S46Rule"
case DHCPv6OptS46BR:
return "S46BR"
case DHCPv6OptS46DMR:
return "S46DMR"
case DHCPv6OptS46V4V4Bind:
return "S46IPv4IPv6AddressBinding"
case DHCPv6OptS46PortParameters:
return "S46PortParameters"
case DHCPv6OptS46ContMAPE:
return "S46MAPEContainer"
case DHCPv6OptS46ContMAPT:
return "S46MAPTContainer"
case DHCPv6OptS46ContLW:
return "S46Lightweight4Over6Container"
case DHCPv6Opt4RD:
return "4RD"
case DHCPv6Opt4RDMapRule:
return "4RDMapRule"
case DHCPv6Opt4RDNonMapRule:
return "4RDNonMapRule"
case DHCPv6OptLQBaseTime:
return "LQBaseTime"
case DHCPv6OptLQStartTime:
return "LQStartTime"
case DHCPv6OptLQEndTime:
return "LQEndTime"
case DHCPv6OptCaptivePortal:
return "CaptivePortal"
case DHCPv6OptMPLParameters:
return "MPLParameterConfiguration"
case DHCPv6OptANIATT:
return "ANIAccessTechnologyType"
case DHCPv6OptANINetworkName:
return "ANINetworkName"
case DHCPv6OptANIAPName:
return "ANIAccessPointName"
case DHCPv6OptANIAPBSSID:
return "ANIAccessPointBSSID"
case DHCPv6OptANIOperatorID:
return "ANIOperatorIdentifier"
case DHCPv6OptANIOperatorRealm:
return "ANIOperatorRealm"
case DHCPv6OptS46Priority:
return "S64Priority"
case DHCPv6OptMUDURLV6:
return "ManufacturerUsageDescriptionURL"
case DHCPv6OptV6Prefix64:
return "V6Prefix64"
case DHCPv6OptFBindingStatus:
return "FailoverBindingStatus"
case DHCPv6OptFConnectFlags:
return "FailoverConnectFlags"
case DHCPv6OptFDNSRemovalInfo:
return "FailoverDNSRemovalInfo"
case DHCPv6OptFDNSHostName:
return "FailoverDNSHostName"
case DHCPv6OptFDNSZoneName:
return "FailoverDNSZoneName"
case DHCPv6OptFDNSFlags:
return "FailoverDNSFlags"
case DHCPv6OptFExpirationTime:
return "FailoverExpirationTime"
case DHCPv6OptFMaxUnacknowledgedBNDUPD:
return "FailoverMaxUnacknowledgedBNDUPDMessages"
case DHCPv6OptFMCLT:
return "FailoverMaximumClientLeadTime"
case DHCPv6OptFPartnerLifetime:
return "FailoverPartnerLifetime"
case DHCPv6OptFPartnerLifetimeSent:
return "FailoverPartnerLifetimeSent"
case DHCPv6OptFPartnerDownTime:
return "FailoverPartnerDownTime"
case DHCPv6OptFPartnerRawCltTime:
return "FailoverPartnerRawClientLeadTime"
case DHCPv6OptFProtocolVersion:
return "FailoverProtocolVersion"
case DHCPv6OptFKeepaliveTime:
return "FailoverKeepaliveTime"
case DHCPv6OptFReconfigureData:
return "FailoverReconfigureData"
case DHCPv6OptFRelationshipName:
return "FailoverRelationshipName"
case DHCPv6OptFServerFlags:
return "FailoverServerFlags"
case DHCPv6OptFServerState:
return "FailoverServerState"
case DHCPv6OptFStartTimeOfState:
return "FailoverStartTimeOfState"
case DHCPv6OptFStateExpirationTime:
return "FailoverStateExpirationTime"
case DHCPv6OptRelayPort:
return "RelayPort"
case DHCPv6OptV6ZeroTouchRedirect:
return "ZeroTouch"
case DHCPv6OptIPV6AddressANDSF:
return "ANDSFIPv6Address"
default:
return fmt.Sprintf("Unknown(%d)", uint16(o))
}
}
// DHCPv6Options is used to get nicely printed option lists which would normally
// be cut off after 5 options.
type DHCPv6Options []DHCPv6Option
// String returns a string version of the options list.
func (o DHCPv6Options) String() string {
buf := &bytes.Buffer{}
buf.WriteByte('[')
for i, opt := range o {
buf.WriteString(opt.String())
if i+1 != len(o) {
buf.WriteString(", ")
}
}
buf.WriteByte(']')
return buf.String()
}
// DHCPv6Option rerpresents a DHCP option.
type DHCPv6Option struct {
Code DHCPv6Opt
Length uint16
Data []byte
}
// String returns a string version of a DHCP Option.
func (o DHCPv6Option) String() string {
switch o.Code {
case DHCPv6OptClientID, DHCPv6OptServerID:
duid, err := decodeDHCPv6DUID(o.Data)
if err != nil {
return fmt.Sprintf("Option(%s:INVALID)", o.Code)
}
return fmt.Sprintf("Option(%s:[%s])", o.Code, duid.String())
case DHCPv6OptOro:
options := ""
for i := 0; i < int(o.Length); i += 2 {
if options != "" {
options += ","
}
option := DHCPv6Opt(binary.BigEndian.Uint16(o.Data[i : i+2]))
options += option.String()
}
return fmt.Sprintf("Option(%s:[%s])", o.Code, options)
default:
return fmt.Sprintf("Option(%s:%v)", o.Code, o.Data)
}
}
// NewDHCPv6Option constructs a new DHCPv6Option with a given type and data.
func NewDHCPv6Option(code DHCPv6Opt, data []byte) DHCPv6Option {
o := DHCPv6Option{Code: code}
if data != nil {
o.Data = data
o.Length = uint16(len(data))
}
return o
}
func (o *DHCPv6Option) encode(b []byte, opts gopacket.SerializeOptions) error {
binary.BigEndian.PutUint16(b[0:2], uint16(o.Code))
if opts.FixLengths {
binary.BigEndian.PutUint16(b[2:4], uint16(len(o.Data)))
} else {
binary.BigEndian.PutUint16(b[2:4], o.Length)
}
copy(b[4:], o.Data)
return nil
}
func (o *DHCPv6Option) decode(data []byte) error {
if len(data) < 2 {
return errors.New("not enough data to decode")
}
o.Code = DHCPv6Opt(binary.BigEndian.Uint16(data[0:2]))
if len(data) < 3 {
return errors.New("not enough data to decode")
}
o.Length = binary.BigEndian.Uint16(data[2:4])
o.Data = data[4 : 4+o.Length]
return nil
}

@ -296,7 +296,7 @@ func (d *DNS) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
if len(data) < 12 {
df.SetTruncated()
return errors.New("DNS packet too short")
return errDNSPacketTooShort
}
// since there are no further layers, the baselayer's content is
@ -366,13 +366,13 @@ func (d *DNS) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
}
if uint16(len(d.Questions)) != d.QDCount {
return errors.New("Invalid query decoding, not the right number of questions")
return errDecodeQueryBadQDCount
} else if uint16(len(d.Answers)) != d.ANCount {
return errors.New("Invalid query decoding, not the right number of answers")
return errDecodeQueryBadANCount
} else if uint16(len(d.Authorities)) != d.NSCount {
return errors.New("Invalid query decoding, not the right number of authorities")
return errDecodeQueryBadNSCount
} else if uint16(len(d.Additionals)) != d.ARCount {
return errors.New("Invalid query decoding, not the right number of additionals info")
return errDecodeQueryBadARCount
}
return nil
}
@ -504,17 +504,15 @@ func (d *DNS) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOpt
return nil
}
var errMaxRecursion = errors.New("max DNS recursion level hit")
const maxRecursionLevel = 255
func decodeName(data []byte, offset int, buffer *[]byte, level int) ([]byte, int, error) {
if level > maxRecursionLevel {
return nil, 0, errMaxRecursion
} else if offset >= len(data) {
return nil, 0, errors.New("dns name offset too high")
return nil, 0, errDNSNameOffsetTooHigh
} else if offset < 0 {
return nil, 0, errors.New("dns name offset is negative")
return nil, 0, errDNSNameOffsetNegative
}
start := len(*buffer)
index := offset
@ -535,9 +533,9 @@ loop:
*/
index2 := index + int(data[index]) + 1
if index2-offset > 255 {
return nil, 0, errors.New("dns name is too long")
return nil, 0, errDNSNameTooLong
} else if index2 < index+1 || index2 > len(data) {
return nil, 0, errors.New("dns name uncomputable: invalid index")
return nil, 0, errDNSNameInvalidIndex
}
*buffer = append(*buffer, '.')
*buffer = append(*buffer, data[index+1:index2]...)
@ -564,11 +562,11 @@ loop:
- a sequence of labels ending with a pointer
*/
if index+2 > len(data) {
return nil, 0, errors.New("dns offset pointer too high")
return nil, 0, errDNSPointerOffsetTooHigh
}
offsetp := int(binary.BigEndian.Uint16(data[index:index+2]) & 0x3fff)
if offsetp > len(data) {
return nil, 0, errors.New("dns offset pointer too high")
return nil, 0, errDNSPointerOffsetTooHigh
}
// This looks a little tricky, but actually isn't. Because of how
// decodeName is written, calling it appends the decoded name to the
@ -590,11 +588,11 @@ loop:
data[index], index)
}
if index >= len(data) {
return nil, 0, errors.New("dns index walked out of range")
return nil, 0, errDNSIndexOutOfRange
}
}
if len(*buffer) <= start {
return nil, 0, errors.New("no dns data found for name")
return nil, 0, errDNSNameHasNoData
}
return (*buffer)[start+1:], index + 1, nil
}
@ -686,7 +684,7 @@ func (rr *DNSResourceRecord) decode(data []byte, offset int, df gopacket.DecodeF
rr.DataLength = binary.BigEndian.Uint16(data[endq+8 : endq+10])
end := endq + 10 + int(rr.DataLength)
if end > len(data) {
return 0, fmt.Errorf("resource record length exceeds data")
return 0, errDecodeRecordLength
}
rr.Data = data[endq+10 : end]
@ -798,7 +796,7 @@ func decodeCharacterStrings(data []byte) ([][]byte, error) {
for index, index2 := 0, 0; index != end; index = index2 {
index2 = index + 1 + int(data[index]) // index increases by 1..256 and does not overflow
if index2 > end {
return nil, errors.New("Insufficient data for a <character-string>")
return nil, errCharStringMissData
}
strings = append(strings, data[index+1:index2])
}
@ -892,3 +890,25 @@ type DNSMX struct {
Preference uint16
Name []byte
}
var (
errMaxRecursion = errors.New("max DNS recursion level hit")
errDNSNameOffsetTooHigh = errors.New("dns name offset too high")
errDNSNameOffsetNegative = errors.New("dns name offset is negative")
errDNSPacketTooShort = errors.New("DNS packet too short")
errDNSNameTooLong = errors.New("dns name is too long")
errDNSNameInvalidIndex = errors.New("dns name uncomputable: invalid index")
errDNSPointerOffsetTooHigh = errors.New("dns offset pointer too high")
errDNSIndexOutOfRange = errors.New("dns index walked out of range")
errDNSNameHasNoData = errors.New("no dns data found for name")
errCharStringMissData = errors.New("Insufficient data for a <character-string>")
errDecodeRecordLength = errors.New("resource record length exceeds data")
errDecodeQueryBadQDCount = errors.New("Invalid query decoding, not the right number of questions")
errDecodeQueryBadANCount = errors.New("Invalid query decoding, not the right number of answers")
errDecodeQueryBadNSCount = errors.New("Invalid query decoding, not the right number of authorities")
errDecodeQueryBadARCount = errors.New("Invalid query decoding, not the right number of additionals info")
)

@ -405,6 +405,12 @@ const (
Dot11InformationElementIDVHTTxPowerEnvelope Dot11InformationElementID = 195
Dot11InformationElementIDChannelSwitchWrapper Dot11InformationElementID = 196
Dot11InformationElementIDOperatingModeNotification Dot11InformationElementID = 199
Dot11InformationElementIDUPSIM Dot11InformationElementID = 200
Dot11InformationElementIDReducedNeighborReport Dot11InformationElementID = 201
Dot11InformationElementIDTVHTOperation Dot11InformationElementID = 202
Dot11InformationElementIDDeviceLocation Dot11InformationElementID = 204
Dot11InformationElementIDWhiteSpaceMap Dot11InformationElementID = 205
Dot11InformationElementIDFineTuningMeasureParams Dot11InformationElementID = 206
Dot11InformationElementIDVendor Dot11InformationElementID = 221
)
@ -742,6 +748,18 @@ func (a Dot11InformationElementID) String() string {
return "Channel Switch Wrapper"
case Dot11InformationElementIDOperatingModeNotification:
return "Operating Mode Notification"
case Dot11InformationElementIDUPSIM:
return "UP SIM"
case Dot11InformationElementIDReducedNeighborReport:
return "Reduced Neighbor Report"
case Dot11InformationElementIDTVHTOperation:
return "TVHT Op"
case Dot11InformationElementIDDeviceLocation:
return "Device Location"
case Dot11InformationElementIDWhiteSpaceMap:
return "White Space Map"
case Dot11InformationElementIDFineTuningMeasureParams:
return "Fine Tuning Measure Parameters"
case Dot11InformationElementIDVendor:
return "Vendor"
default:

@ -10,6 +10,7 @@ package layers
import (
"errors"
"fmt"
"runtime"
"github.com/google/gopacket"
)
@ -49,6 +50,7 @@ const (
EthernetTypeNortelDiscovery EthernetType = 0x01a2
EthernetTypeTransparentEthernetBridging EthernetType = 0x6558
EthernetTypeDot1Q EthernetType = 0x8100
EthernetTypePPP EthernetType = 0x880b
EthernetTypePPPoEDiscovery EthernetType = 0x8863
EthernetTypePPPoESession EthernetType = 0x8864
EthernetTypeMPLSUnicast EthernetType = 0x8847
@ -310,6 +312,7 @@ func initActualTypeData() {
EthernetTypeMetadata[EthernetTypeIPv6] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeIPv6), Name: "IPv6", LayerType: LayerTypeIPv6}
EthernetTypeMetadata[EthernetTypeARP] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeARP), Name: "ARP", LayerType: LayerTypeARP}
EthernetTypeMetadata[EthernetTypeDot1Q] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot1Q), Name: "Dot1Q", LayerType: LayerTypeDot1Q}
EthernetTypeMetadata[EthernetTypePPP] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodePPP), Name: "PPP", LayerType: LayerTypePPP}
EthernetTypeMetadata[EthernetTypePPPoEDiscovery] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodePPPoE), Name: "PPPoEDiscovery", LayerType: LayerTypePPPoE}
EthernetTypeMetadata[EthernetTypePPPoESession] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodePPPoE), Name: "PPPoESession", LayerType: LayerTypePPPoE}
EthernetTypeMetadata[EthernetTypeEthernetCTP] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeEthernetCTP), Name: "EthernetCTP", LayerType: LayerTypeEthernetCTP}
@ -375,6 +378,13 @@ func initActualTypeData() {
LinkTypeMetadata[LinkTypeLoop] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeLoopback), Name: "Loop"}
LinkTypeMetadata[LinkTypeIEEE802_11] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11), Name: "802.11"}
LinkTypeMetadata[LinkTypeRaw] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeIPv4or6), Name: "Raw"}
// See https://github.com/the-tcpdump-group/libpcap/blob/170f717e6e818cdc4bcbbfd906b63088eaa88fa0/pcap/dlt.h#L85
// Or https://github.com/wireshark/wireshark/blob/854cfe53efe44080609c78053ecfb2342ad84a08/wiretap/pcap-common.c#L508
if runtime.GOOS == "openbsd" {
LinkTypeMetadata[14] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeIPv4or6), Name: "Raw"}
} else {
LinkTypeMetadata[12] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeIPv4or6), Name: "Raw"}
}
LinkTypeMetadata[LinkTypePFLog] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodePFLog), Name: "PFLog"}
LinkTypeMetadata[LinkTypeIEEE80211Radio] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeRadioTap), Name: "RadioTap"}
LinkTypeMetadata[LinkTypeLinuxUSB] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeUSB), Name: "USB"}

@ -15,11 +15,11 @@ import (
// GRE is a Generic Routing Encapsulation header.
type GRE struct {
BaseLayer
ChecksumPresent, RoutingPresent, KeyPresent, SeqPresent, StrictSourceRoute bool
RecursionControl, Flags, Version uint8
Protocol EthernetType
Checksum, Offset uint16
Key, Seq uint32
ChecksumPresent, RoutingPresent, KeyPresent, SeqPresent, StrictSourceRoute, AckPresent bool
RecursionControl, Flags, Version uint8
Protocol EthernetType
Checksum, Offset uint16
Key, Seq, Ack uint32
*GRERouting
}
@ -42,6 +42,7 @@ func (g *GRE) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
g.KeyPresent = data[0]&0x20 != 0
g.SeqPresent = data[0]&0x10 != 0
g.StrictSourceRoute = data[0]&0x08 != 0
g.AckPresent = data[1]&0x80 != 0
g.RecursionControl = data[0] & 0x7
g.Flags = data[1] >> 3
g.Version = data[1] & 0x7
@ -77,6 +78,10 @@ func (g *GRE) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
tail = &sre.Next
}
}
if g.AckPresent {
g.Ack = binary.BigEndian.Uint32(data[offset : offset+4])
offset += 4
}
g.BaseLayer = BaseLayer{data[:offset], data[offset:]}
return nil
}
@ -102,6 +107,9 @@ func (g *GRE) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOpt
}
size += 4
}
if g.AckPresent {
size += 4
}
buf, err := b.PrependBytes(size)
if err != nil {
return err
@ -124,6 +132,9 @@ func (g *GRE) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOpt
if g.StrictSourceRoute {
buf[0] |= 0x08
}
if g.AckPresent {
buf[1] |= 0x80
}
buf[0] |= g.RecursionControl
buf[1] |= g.Flags << 3
buf[1] |= g.Version
@ -159,6 +170,10 @@ func (g *GRE) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOpt
// Terminate routing field with a "NULL" SRE.
binary.BigEndian.PutUint32(buf[offset:offset+4], 0)
}
if g.AckPresent {
binary.BigEndian.PutUint32(buf[offset:offset+4], g.Ack)
offset += 4
}
if g.ChecksumPresent {
if opts.ComputeChecksums {
g.Checksum = tcpipChecksum(b.Bytes(), 0)

@ -129,6 +129,11 @@ func (i ICMPv6Opt) String() string {
}
}
// CanDecode returns the set of layer types that this DecodingLayer can decode.
func (i *ICMPv6Echo) CanDecode() gopacket.LayerClass {
return LayerTypeICMPv6Echo
}
// LayerType returns LayerTypeICMPv6Echo.
func (i *ICMPv6Echo) LayerType() gopacket.LayerType {
return LayerTypeICMPv6Echo

@ -186,6 +186,10 @@ func (ip *IPv4) flagsfrags() (ff uint16) {
// DecodeFromBytes decodes the given bytes into this layer.
func (ip *IPv4) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
if len(data) < 20 {
df.SetTruncated()
return fmt.Errorf("Invalid ip4 header. Length %d less than 20", len(data))
}
flagsfrags := binary.BigEndian.Uint16(data[6:8])
ip.Version = uint8(data[0]) >> 4
@ -201,6 +205,7 @@ func (ip *IPv4) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
ip.SrcIP = data[12:16]
ip.DstIP = data[16:20]
ip.Options = ip.Options[:0]
ip.Padding = nil
// Set up an initial guess for contents/payload... we'll reset these soon.
ip.BaseLayer = BaseLayer{Contents: data}
@ -243,19 +248,28 @@ func (ip *IPv4) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
opt.OptionLength = 1
ip.Options = append(ip.Options, opt)
ip.Padding = data[1:]
break
return nil
case 1: // 1 byte padding
opt.OptionLength = 1
data = data[1:]
ip.Options = append(ip.Options, opt)
default:
if len(data) < 2 {
df.SetTruncated()
return fmt.Errorf("Invalid ip4 option length. Length %d less than 2", len(data))
}
opt.OptionLength = data[1]
if len(data) < int(opt.OptionLength) {
df.SetTruncated()
return fmt.Errorf("IP option length exceeds remaining IP header size, option type %v length %v", opt.OptionType, opt.OptionLength)
}
if opt.OptionLength <= 2 {
return fmt.Errorf("Invalid IP option type %v length %d. Must be greater than 2", opt.OptionType, opt.OptionLength)
}
opt.OptionData = data[2:opt.OptionLength]
}
if len(data) >= int(opt.OptionLength) {
data = data[opt.OptionLength:]
} else {
return fmt.Errorf("IP option length exceeds remaining IP header size, option type %v length %v", opt.OptionType, opt.OptionLength)
ip.Options = append(ip.Options, opt)
}
ip.Options = append(ip.Options, opt)
}
return nil
}

@ -219,6 +219,10 @@ func (ipv6 *IPv6) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.Serializ
// DecodeFromBytes implementation according to gopacket.DecodingLayer
func (ipv6 *IPv6) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
if len(data) < 40 {
df.SetTruncated()
return fmt.Errorf("Invalid ip6 header. Length %d less than 40", len(data))
}
ipv6.Version = uint8(data[0]) >> 4
ipv6.TrafficClass = uint8((binary.BigEndian.Uint16(data[0:2]) >> 4) & 0x00FF)
ipv6.FlowLabel = binary.BigEndian.Uint32(data[0:4]) & 0x000FFFFF
@ -403,10 +407,17 @@ type ipv6ExtensionBase struct {
ActualLength int
}
func decodeIPv6ExtensionBase(data []byte) (i ipv6ExtensionBase) {
func decodeIPv6ExtensionBase(data []byte, df gopacket.DecodeFeedback) (i ipv6ExtensionBase, returnedErr error) {
if len(data) < 2 {
df.SetTruncated()
return ipv6ExtensionBase{}, fmt.Errorf("Invalid ip6-extension header. Length %d less than 2", len(data))
}
i.NextHeader = IPProtocol(data[0])
i.HeaderLength = data[1]
i.ActualLength = int(i.HeaderLength)*8 + 8
if len(data) < i.ActualLength {
return ipv6ExtensionBase{}, fmt.Errorf("Invalid ip6-extension header. Length %d less than specified length %d", len(data), i.ActualLength)
}
i.Contents = data[:i.ActualLength]
i.Payload = data[i.ActualLength:]
return
@ -422,7 +433,10 @@ type IPv6ExtensionSkipper struct {
// DecodeFromBytes implementation according to gopacket.DecodingLayer
func (i *IPv6ExtensionSkipper) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
extension := decodeIPv6ExtensionBase(data)
extension, err := decodeIPv6ExtensionBase(data, df)
if err != nil {
return err
}
i.BaseLayer = BaseLayer{data[:extension.ActualLength], data[extension.ActualLength:]}
i.NextHeader = extension.NextHeader
return nil
@ -485,7 +499,11 @@ func (i *IPv6HopByHop) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.Ser
// DecodeFromBytes implementation according to gopacket.DecodingLayer
func (i *IPv6HopByHop) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
i.ipv6ExtensionBase = decodeIPv6ExtensionBase(data)
var err error
i.ipv6ExtensionBase, err = decodeIPv6ExtensionBase(data, df)
if err != nil {
return err
}
offset := 2
for offset < i.ActualLength {
opt := decodeIPv6HeaderTLVOption(data[offset:])
@ -534,8 +552,12 @@ type IPv6Routing struct {
func (i *IPv6Routing) LayerType() gopacket.LayerType { return LayerTypeIPv6Routing }
func decodeIPv6Routing(data []byte, p gopacket.PacketBuilder) error {
base, err := decodeIPv6ExtensionBase(data, p)
if err != nil {
return err
}
i := &IPv6Routing{
ipv6ExtensionBase: decodeIPv6ExtensionBase(data),
ipv6ExtensionBase: base,
RoutingType: data[2],
SegmentsLeft: data[3],
Reserved: data[4:8],
@ -573,6 +595,10 @@ type IPv6Fragment struct {
func (i *IPv6Fragment) LayerType() gopacket.LayerType { return LayerTypeIPv6Fragment }
func decodeIPv6Fragment(data []byte, p gopacket.PacketBuilder) error {
if len(data) < 8 {
p.SetTruncated()
return fmt.Errorf("Invalid ip6-fragment header. Length %d less than 8", len(data))
}
i := &IPv6Fragment{
BaseLayer: BaseLayer{data[:8], data[8:]},
NextHeader: IPProtocol(data[0]),
@ -600,7 +626,11 @@ func (i *IPv6Destination) LayerType() gopacket.LayerType { return LayerTypeIPv6D
// DecodeFromBytes implementation according to gopacket.DecodingLayer
func (i *IPv6Destination) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
i.ipv6ExtensionBase = decodeIPv6ExtensionBase(data)
var err error
i.ipv6ExtensionBase, err = decodeIPv6ExtensionBase(data, df)
if err != nil {
return err
}
offset := 2
for offset < i.ActualLength {
opt := decodeIPv6HeaderTLVOption(data[offset:])

@ -11,7 +11,6 @@ import (
)
var (
// Pending to set the proper number
LayerTypeARP = gopacket.RegisterLayerType(10, gopacket.LayerTypeMetadata{Name: "ARP", Decoder: gopacket.DecodeFunc(decodeARP)})
LayerTypeCiscoDiscovery = gopacket.RegisterLayerType(11, gopacket.LayerTypeMetadata{Name: "CiscoDiscovery", Decoder: gopacket.DecodeFunc(decodeCiscoDiscovery)})
LayerTypeEthernetCTP = gopacket.RegisterLayerType(12, gopacket.LayerTypeMetadata{Name: "EthernetCTP", Decoder: gopacket.DecodeFunc(decodeEthernetCTP)})
@ -143,6 +142,7 @@ var (
LayerTypeMLDv2MulticastListenerReport = gopacket.RegisterLayerType(138, gopacket.LayerTypeMetadata{Name: "MLDv2MulticastListenerReport", Decoder: gopacket.DecodeFunc(decodeMLDv2MulticastListenerReport)})
LayerTypeMLDv2MulticastListenerQuery = gopacket.RegisterLayerType(139, gopacket.LayerTypeMetadata{Name: "MLDv2MulticastListenerQuery", Decoder: gopacket.DecodeFunc(decodeMLDv2MulticastListenerQuery)})
LayerTypeTLS = gopacket.RegisterLayerType(140, gopacket.LayerTypeMetadata{Name: "TLS", Decoder: gopacket.DecodeFunc(decodeTLS)})
LayerTypeModbusTCP = gopacket.RegisterLayerType(141, gopacket.LayerTypeMetadata{Name: "ModbusTCP", Decoder: gopacket.DecodeFunc(decodeModbusTCP)})
)
var (

@ -54,6 +54,7 @@ type LinuxSLL struct {
AddrLen uint16
Addr net.HardwareAddr
EthernetType EthernetType
AddrType uint16
}
// LayerType returns LayerTypeLinuxSLL.
@ -76,6 +77,7 @@ func (sll *LinuxSLL) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) er
return errors.New("Linux SLL packet too small")
}
sll.PacketType = LinuxSLLPacketType(binary.BigEndian.Uint16(data[0:2]))
sll.AddrType = binary.BigEndian.Uint16(data[2:4])
sll.AddrLen = binary.BigEndian.Uint16(data[4:6])
sll.Addr = net.HardwareAddr(data[6 : sll.AddrLen+6])

150
vendor/github.com/google/gopacket/layers/modbustcp.go generated vendored Normal file

@ -0,0 +1,150 @@
// 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 layers
import (
"encoding/binary"
"errors"
"github.com/google/gopacket"
)
//******************************************************************************
//
// ModbusTCP Decoding Layer
// ------------------------------------------
// This file provides a GoPacket decoding layer for ModbusTCP.
//
//******************************************************************************
const mbapRecordSizeInBytes int = 7
const modbusPDUMinimumRecordSizeInBytes int = 2
const modbusPDUMaximumRecordSizeInBytes int = 253
// ModbusProtocol type
type ModbusProtocol uint16
// ModbusProtocol known values.
const (
ModbusProtocolModbus ModbusProtocol = 0
)
func (mp ModbusProtocol) String() string {
switch mp {
default:
return "Unknown"
case ModbusProtocolModbus:
return "Modbus"
}
}
//******************************************************************************
// ModbusTCP Type
// --------
// Type ModbusTCP implements the DecodingLayer interface. Each ModbusTCP object
// represents in a structured form the MODBUS Application Protocol header (MBAP) record present as the TCP
// payload in an ModbusTCP TCP packet.
//
type ModbusTCP struct {
BaseLayer // Stores the packet bytes and payload (Modbus PDU) bytes .
TransactionIdentifier uint16 // Identification of a MODBUS Request/Response transaction
ProtocolIdentifier ModbusProtocol // It is used for intra-system multiplexing
Length uint16 // Number of following bytes (includes 1 byte for UnitIdentifier + Modbus data length
UnitIdentifier uint8 // Identification of a remote slave connected on a serial line or on other buses
}
//******************************************************************************
// LayerType returns the layer type of the ModbusTCP object, which is LayerTypeModbusTCP.
func (d *ModbusTCP) LayerType() gopacket.LayerType {
return LayerTypeModbusTCP
}
//******************************************************************************
// decodeModbusTCP analyses a byte slice and attempts to decode it as an ModbusTCP
// record of a TCP packet.
//
// If it succeeds, it loads p with information about the packet and returns nil.
// If it fails, it returns an error (non nil).
//
// This function is employed in layertypes.go to register the ModbusTCP layer.
func decodeModbusTCP(data []byte, p gopacket.PacketBuilder) error {
// Attempt to decode the byte slice.
d := &ModbusTCP{}
err := d.DecodeFromBytes(data, p)
if err != nil {
return err
}
// If the decoding worked, add the layer to the packet and set it
// as the application layer too, if there isn't already one.
p.AddLayer(d)
p.SetApplicationLayer(d)
return p.NextDecoder(d.NextLayerType())
}
//******************************************************************************
// DecodeFromBytes analyses a byte slice and attempts to decode it as an ModbusTCP
// record of a TCP packet.
//
// Upon succeeds, it loads the ModbusTCP object with information about the packet
// and returns nil.
// Upon failure, it returns an error (non nil).
func (d *ModbusTCP) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
// If the data block is too short to be a MBAP record, then return an error.
if len(data) < mbapRecordSizeInBytes+modbusPDUMinimumRecordSizeInBytes {
df.SetTruncated()
return errors.New("ModbusTCP packet too short")
}
if len(data) > mbapRecordSizeInBytes+modbusPDUMaximumRecordSizeInBytes {
df.SetTruncated()
return errors.New("ModbusTCP packet too long")
}
// ModbusTCP type embeds type BaseLayer which contains two fields:
// Contents is supposed to contain the bytes of the data at this level (MPBA).
// Payload is supposed to contain the payload of this level (PDU).
d.BaseLayer = BaseLayer{Contents: data[:mbapRecordSizeInBytes], Payload: data[mbapRecordSizeInBytes:len(data)]}
// Extract the fields from the block of bytes.
// The fields can just be copied in big endian order.
d.TransactionIdentifier = binary.BigEndian.Uint16(data[:2])
d.ProtocolIdentifier = ModbusProtocol(binary.BigEndian.Uint16(data[2:4]))
d.Length = binary.BigEndian.Uint16(data[4:6])
// Length should have the size of the payload plus one byte (size of UnitIdentifier)
if d.Length != uint16(len(d.BaseLayer.Payload)+1) {
df.SetTruncated()
return errors.New("ModbusTCP packet with wrong field value (Length)")
}
d.UnitIdentifier = uint8(data[6])
return nil
}
//******************************************************************************
// NextLayerType returns the layer type of the ModbusTCP payload, which is LayerTypePayload.
func (d *ModbusTCP) NextLayerType() gopacket.LayerType {
return gopacket.LayerTypePayload
}
//******************************************************************************
// Payload returns Modbus Protocol Data Unit (PDU) composed by Function Code and Data, it is carried within ModbusTCP packets
func (d *ModbusTCP) Payload() []byte {
return d.BaseLayer.Payload
}

@ -61,15 +61,16 @@ func (a TCPPort) LayerType() gopacket.LayerType {
var tcpPortLayerType = [65536]gopacket.LayerType{
53: LayerTypeDNS,
443: LayerTypeTLS, // https
636: LayerTypeTLS, // ldaps
989: LayerTypeTLS, // ftps-data
990: LayerTypeTLS, // ftps
992: LayerTypeTLS, // telnets
993: LayerTypeTLS, // imaps
994: LayerTypeTLS, // ircs
995: LayerTypeTLS, // pop3s
5061: LayerTypeTLS, // ips
443: LayerTypeTLS, // https
502: LayerTypeModbusTCP, // modbustcp
636: LayerTypeTLS, // ldaps
989: LayerTypeTLS, // ftps-data
990: LayerTypeTLS, // ftps
992: LayerTypeTLS, // telnets
993: LayerTypeTLS, // imaps
994: LayerTypeTLS, // ircs
995: LayerTypeTLS, // pop3s
5061: LayerTypeTLS, // ips
}
// RegisterTCPPortLayerType creates a new mapping between a TCPPort

@ -15,7 +15,8 @@ import (
// PPP is the layer for PPP encapsulation headers.
type PPP struct {
BaseLayer
PPPType PPPType
PPPType PPPType
HasPPTPHeader bool
}
// PPPEndpoint is a singleton endpoint for PPP. Since there is no actual
@ -36,17 +37,22 @@ func (p *PPP) LinkFlow() gopacket.Flow { return PPPFlow }
func decodePPP(data []byte, p gopacket.PacketBuilder) error {
ppp := &PPP{}
if data[0]&0x1 == 0 {
if data[1]&0x1 == 0 {
offset := 0
if data[0] == 0xff && data[1] == 0x03 {
offset = 2
ppp.HasPPTPHeader = true
}
if data[offset]&0x1 == 0 {
if data[offset+1]&0x1 == 0 {
return errors.New("PPP has invalid type")
}
ppp.PPPType = PPPType(binary.BigEndian.Uint16(data[:2]))
ppp.Contents = data[:2]
ppp.Payload = data[2:]
ppp.PPPType = PPPType(binary.BigEndian.Uint16(data[offset : offset+2]))
ppp.Contents = data[offset : offset+2]
ppp.Payload = data[offset+2:]
} else {
ppp.PPPType = PPPType(data[0])
ppp.Contents = data[:1]
ppp.Payload = data[1:]
ppp.PPPType = PPPType(data[offset])
ppp.Contents = data[offset : offset+1]
ppp.Payload = data[offset+1:]
}
p.AddLayer(ppp)
p.SetLinkLayer(ppp)
@ -70,5 +76,13 @@ func (p *PPP) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOpt
}
bytes[0] = uint8(p.PPPType)
}
if p.HasPPTPHeader {
bytes, err := b.PrependBytes(2)
if err != nil {
return err
}
bytes[0] = 0xff
bytes[1] = 0x03
}
return nil
}

@ -486,139 +486,148 @@ func decodeFlowSample(data *[]byte, expanded bool) (SFlowFlowSample, error) {
for i := uint32(0); i < s.RecordCount; i++ {
rdf := SFlowFlowDataFormat(binary.BigEndian.Uint32((*data)[:4]))
_, flowRecordType := rdf.decode()
enterpriseID, flowRecordType := rdf.decode()
switch flowRecordType {
case SFlowTypeRawPacketFlow:
if record, err := decodeRawPacketFlowRecord(data); err == nil {
s.Records = append(s.Records, record)
} else {
return s, err
// Try to decode when EnterpriseID is 0 signaling
// default sflow structs are used according specification
// Unexpected behavior detected for e.g. with pmacct
if enterpriseID == 0 {
switch flowRecordType {
case SFlowTypeRawPacketFlow:
if record, err := decodeRawPacketFlowRecord(data); err == nil {
s.Records = append(s.Records, record)
} else {
return s, err
}
case SFlowTypeExtendedUserFlow:
if record, err := decodeExtendedUserFlow(data); err == nil {
s.Records = append(s.Records, record)
} else {
return s, err
}
case SFlowTypeExtendedUrlFlow:
if record, err := decodeExtendedURLRecord(data); err == nil {
s.Records = append(s.Records, record)
} else {
return s, err
}
case SFlowTypeExtendedSwitchFlow:
if record, err := decodeExtendedSwitchFlowRecord(data); err == nil {
s.Records = append(s.Records, record)
} else {
return s, err
}
case SFlowTypeExtendedRouterFlow:
if record, err := decodeExtendedRouterFlowRecord(data); err == nil {
s.Records = append(s.Records, record)
} else {
return s, err
}
case SFlowTypeExtendedGatewayFlow:
if record, err := decodeExtendedGatewayFlowRecord(data); err == nil {
s.Records = append(s.Records, record)
} else {
return s, err
}
case SFlowTypeEthernetFrameFlow:
if record, err := decodeEthernetFrameFlowRecord(data); err == nil {
s.Records = append(s.Records, record)
} else {
return s, err
}
case SFlowTypeIpv4Flow:
if record, err := decodeSFlowIpv4Record(data); err == nil {
s.Records = append(s.Records, record)
} else {
return s, err
}
case SFlowTypeIpv6Flow:
if record, err := decodeSFlowIpv6Record(data); err == nil {
s.Records = append(s.Records, record)
} else {
return s, err
}
case SFlowTypeExtendedMlpsFlow:
// TODO
skipRecord(data)
return s, errors.New("skipping TypeExtendedMlpsFlow")
case SFlowTypeExtendedNatFlow:
// TODO
skipRecord(data)
return s, errors.New("skipping TypeExtendedNatFlow")
case SFlowTypeExtendedMlpsTunnelFlow:
// TODO
skipRecord(data)
return s, errors.New("skipping TypeExtendedMlpsTunnelFlow")
case SFlowTypeExtendedMlpsVcFlow:
// TODO
skipRecord(data)
return s, errors.New("skipping TypeExtendedMlpsVcFlow")
case SFlowTypeExtendedMlpsFecFlow:
// TODO
skipRecord(data)
return s, errors.New("skipping TypeExtendedMlpsFecFlow")
case SFlowTypeExtendedMlpsLvpFecFlow:
// TODO
skipRecord(data)
return s, errors.New("skipping TypeExtendedMlpsLvpFecFlow")
case SFlowTypeExtendedVlanFlow:
// TODO
skipRecord(data)
return s, errors.New("skipping TypeExtendedVlanFlow")
case SFlowTypeExtendedIpv4TunnelEgressFlow:
if record, err := decodeExtendedIpv4TunnelEgress(data); err == nil {
s.Records = append(s.Records, record)
} else {
return s, err
}
case SFlowTypeExtendedIpv4TunnelIngressFlow:
if record, err := decodeExtendedIpv4TunnelIngress(data); err == nil {
s.Records = append(s.Records, record)
} else {
return s, err
}
case SFlowTypeExtendedIpv6TunnelEgressFlow:
if record, err := decodeExtendedIpv6TunnelEgress(data); err == nil {
s.Records = append(s.Records, record)
} else {
return s, err
}
case SFlowTypeExtendedIpv6TunnelIngressFlow:
if record, err := decodeExtendedIpv6TunnelIngress(data); err == nil {
s.Records = append(s.Records, record)
} else {
return s, err
}
case SFlowTypeExtendedDecapsulateEgressFlow:
if record, err := decodeExtendedDecapsulateEgress(data); err == nil {
s.Records = append(s.Records, record)
} else {
return s, err
}
case SFlowTypeExtendedDecapsulateIngressFlow:
if record, err := decodeExtendedDecapsulateIngress(data); err == nil {
s.Records = append(s.Records, record)
} else {
return s, err
}
case SFlowTypeExtendedVniEgressFlow:
if record, err := decodeExtendedVniEgress(data); err == nil {
s.Records = append(s.Records, record)
} else {
return s, err
}
case SFlowTypeExtendedVniIngressFlow:
if record, err := decodeExtendedVniIngress(data); err == nil {
s.Records = append(s.Records, record)
} else {
return s, err
}
default:
return s, fmt.Errorf("Unsupported flow record type: %d", flowRecordType)
}
case SFlowTypeExtendedUserFlow:
if record, err := decodeExtendedUserFlow(data); err == nil {
s.Records = append(s.Records, record)
} else {
return s, err
}
case SFlowTypeExtendedUrlFlow:
if record, err := decodeExtendedURLRecord(data); err == nil {
s.Records = append(s.Records, record)
} else {
return s, err
}
case SFlowTypeExtendedSwitchFlow:
if record, err := decodeExtendedSwitchFlowRecord(data); err == nil {
s.Records = append(s.Records, record)
} else {
return s, err
}
case SFlowTypeExtendedRouterFlow:
if record, err := decodeExtendedRouterFlowRecord(data); err == nil {
s.Records = append(s.Records, record)
} else {
return s, err
}
case SFlowTypeExtendedGatewayFlow:
if record, err := decodeExtendedGatewayFlowRecord(data); err == nil {
s.Records = append(s.Records, record)
} else {
return s, err
}
case SFlowTypeEthernetFrameFlow:
// TODO
} else {
skipRecord(data)
return s, errors.New("skipping TypeEthernetFrameFlow")
case SFlowTypeIpv4Flow:
if record, err := decodeSFlowIpv4Record(data); err == nil {
s.Records = append(s.Records, record)
} else {
return s, err
}
case SFlowTypeIpv6Flow:
if record, err := decodeSFlowIpv6Record(data); err == nil {
s.Records = append(s.Records, record)
} else {
return s, err
}
case SFlowTypeExtendedMlpsFlow:
// TODO
skipRecord(data)
return s, errors.New("skipping TypeExtendedMlpsFlow")
case SFlowTypeExtendedNatFlow:
// TODO
skipRecord(data)
return s, errors.New("skipping TypeExtendedNatFlow")
case SFlowTypeExtendedMlpsTunnelFlow:
// TODO
skipRecord(data)
return s, errors.New("skipping TypeExtendedMlpsTunnelFlow")
case SFlowTypeExtendedMlpsVcFlow:
// TODO
skipRecord(data)
return s, errors.New("skipping TypeExtendedMlpsVcFlow")
case SFlowTypeExtendedMlpsFecFlow:
// TODO
skipRecord(data)
return s, errors.New("skipping TypeExtendedMlpsFecFlow")
case SFlowTypeExtendedMlpsLvpFecFlow:
// TODO
skipRecord(data)
return s, errors.New("skipping TypeExtendedMlpsLvpFecFlow")
case SFlowTypeExtendedVlanFlow:
// TODO
skipRecord(data)
return s, errors.New("skipping TypeExtendedVlanFlow")
case SFlowTypeExtendedIpv4TunnelEgressFlow:
if record, err := decodeExtendedIpv4TunnelEgress(data); err == nil {
s.Records = append(s.Records, record)
} else {
return s, err
}
case SFlowTypeExtendedIpv4TunnelIngressFlow:
if record, err := decodeExtendedIpv4TunnelIngress(data); err == nil {
s.Records = append(s.Records, record)
} else {
return s, err
}
case SFlowTypeExtendedIpv6TunnelEgressFlow:
if record, err := decodeExtendedIpv6TunnelEgress(data); err == nil {
s.Records = append(s.Records, record)
} else {
return s, err
}
case SFlowTypeExtendedIpv6TunnelIngressFlow:
if record, err := decodeExtendedIpv6TunnelIngress(data); err == nil {
s.Records = append(s.Records, record)
} else {
return s, err
}
case SFlowTypeExtendedDecapsulateEgressFlow:
if record, err := decodeExtendedDecapsulateEgress(data); err == nil {
s.Records = append(s.Records, record)
} else {
return s, err
}
case SFlowTypeExtendedDecapsulateIngressFlow:
if record, err := decodeExtendedDecapsulateIngress(data); err == nil {
s.Records = append(s.Records, record)
} else {
return s, err
}
case SFlowTypeExtendedVniEgressFlow:
if record, err := decodeExtendedVniEgress(data); err == nil {
s.Records = append(s.Records, record)
} else {
return s, err
}
case SFlowTypeExtendedVniIngressFlow:
if record, err := decodeExtendedVniIngress(data); err == nil {
s.Records = append(s.Records, record)
} else {
return s, err
}
default:
return s, fmt.Errorf("Unsupported flow record type: %d", flowRecordType)
}
}
return s, nil
@ -684,7 +693,12 @@ const (
SFlowTypeTokenRingInterfaceCounters SFlowCounterRecordType = 3
SFlowType100BaseVGInterfaceCounters SFlowCounterRecordType = 4
SFlowTypeVLANCounters SFlowCounterRecordType = 5
SFlowTypeLACPCounters SFlowCounterRecordType = 7
SFlowTypeProcessorCounters SFlowCounterRecordType = 1001
SFlowTypeOpenflowPortCounters SFlowCounterRecordType = 1004
SFlowTypePORTNAMECounters SFlowCounterRecordType = 1005
SFLowTypeAPPRESOURCESCounters SFlowCounterRecordType = 2203
SFlowTypeOVSDPCounters SFlowCounterRecordType = 2207
)
func (cr SFlowCounterRecordType) String() string {
@ -699,8 +713,18 @@ func (cr SFlowCounterRecordType) String() string {
return "100BaseVG Interface Counters"
case SFlowTypeVLANCounters:
return "VLAN Counters"
case SFlowTypeLACPCounters:
return "LACP Counters"
case SFlowTypeProcessorCounters:
return "Processor Counters"
case SFlowTypeOpenflowPortCounters:
return "Openflow Port Counters"
case SFlowTypePORTNAMECounters:
return "PORT NAME Counters"
case SFLowTypeAPPRESOURCESCounters:
return "App Resources Counters"
case SFlowTypeOVSDPCounters:
return "OVSDP Counters"
default:
return ""
@ -749,14 +773,47 @@ func decodeCounterSample(data *[]byte, expanded bool) (SFlowCounterSample, error
skipRecord(data)
return s, errors.New("skipping Type100BaseVGInterfaceCounters")
case SFlowTypeVLANCounters:
skipRecord(data)
return s, errors.New("skipping TypeVLANCounters")
if record, err := decodeVLANCounters(data); err == nil {
s.Records = append(s.Records, record)
} else {
return s, err
}
case SFlowTypeLACPCounters:
if record, err := decodeLACPCounters(data); err == nil {
s.Records = append(s.Records, record)
} else {
return s, err
}
case SFlowTypeProcessorCounters:
if record, err := decodeProcessorCounters(data); err == nil {
s.Records = append(s.Records, record)
} else {
return s, err
}
case SFlowTypeOpenflowPortCounters:
if record, err := decodeOpenflowportCounters(data); err == nil {
s.Records = append(s.Records, record)
} else {
return s, err
}
case SFlowTypePORTNAMECounters:
if record, err := decodePortnameCounters(data); err == nil {
s.Records = append(s.Records, record)
} else {
return s, err
}
case SFLowTypeAPPRESOURCESCounters:
if record, err := decodeAppresourcesCounters(data); err == nil {
s.Records = append(s.Records, record)
} else {
return s, err
}
case SFlowTypeOVSDPCounters:
if record, err := decodeOVSDPCounters(data); err == nil {
s.Records = append(s.Records, record)
} else {
return s, err
}
default:
return s, fmt.Errorf("Invalid counter record type: %d", counterRecordType)
}
@ -1970,9 +2027,18 @@ func (bcr SFlowBaseCounterRecord) GetType() SFlowCounterRecordType {
return SFlowType100BaseVGInterfaceCounters
case SFlowTypeVLANCounters:
return SFlowTypeVLANCounters
case SFlowTypeLACPCounters:
return SFlowTypeLACPCounters
case SFlowTypeProcessorCounters:
return SFlowTypeProcessorCounters
case SFlowTypeOpenflowPortCounters:
return SFlowTypeOpenflowPortCounters
case SFlowTypePORTNAMECounters:
return SFlowTypePORTNAMECounters
case SFLowTypeAPPRESOURCESCounters:
return SFLowTypeAPPRESOURCESCounters
case SFlowTypeOVSDPCounters:
return SFlowTypeOVSDPCounters
}
unrecognized := fmt.Sprint("Unrecognized counter record type:", bcr.Format)
panic(unrecognized)
@ -2135,6 +2201,83 @@ func decodeEthernetCounters(data *[]byte) (SFlowEthernetCounters, error) {
return ec, nil
}
// VLAN Counter
type SFlowVLANCounters struct {
SFlowBaseCounterRecord
VlanID uint32
Octets uint64
UcastPkts uint32
MulticastPkts uint32
BroadcastPkts uint32
Discards uint32
}
func decodeVLANCounters(data *[]byte) (SFlowVLANCounters, error) {
vc := SFlowVLANCounters{}
var cdf SFlowCounterDataFormat
*data, cdf = (*data)[4:], SFlowCounterDataFormat(binary.BigEndian.Uint32((*data)[:4]))
vc.EnterpriseID, vc.Format = cdf.decode()
vc.EnterpriseID, vc.Format = cdf.decode()
*data, vc.FlowDataLength = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
*data, vc.VlanID = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
*data, vc.Octets = (*data)[8:], binary.BigEndian.Uint64((*data)[:8])
*data, vc.UcastPkts = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
*data, vc.MulticastPkts = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
*data, vc.BroadcastPkts = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
*data, vc.Discards = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
return vc, nil
}
//SFLLACPportState : SFlow LACP Port State (All(4) - 32 bit)
type SFLLACPPortState struct {
PortStateAll uint32
}
//LACPcounters : LACP SFlow Counters ( 64 Bytes )
type SFlowLACPCounters struct {
SFlowBaseCounterRecord
ActorSystemID net.HardwareAddr
PartnerSystemID net.HardwareAddr
AttachedAggID uint32
LacpPortState SFLLACPPortState
LACPDUsRx uint32
MarkerPDUsRx uint32
MarkerResponsePDUsRx uint32
UnknownRx uint32
IllegalRx uint32
LACPDUsTx uint32
MarkerPDUsTx uint32
MarkerResponsePDUsTx uint32
}
func decodeLACPCounters(data *[]byte) (SFlowLACPCounters, error) {
la := SFlowLACPCounters{}
var cdf SFlowCounterDataFormat
*data, cdf = (*data)[4:], SFlowCounterDataFormat(binary.BigEndian.Uint32((*data)[:4]))
la.EnterpriseID, la.Format = cdf.decode()
*data, la.FlowDataLength = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
*data, la.ActorSystemID = (*data)[6:], (*data)[:6]
*data = (*data)[2:] // remove padding
*data, la.PartnerSystemID = (*data)[6:], (*data)[:6]
*data = (*data)[2:] //remove padding
*data, la.AttachedAggID = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
*data, la.LacpPortState.PortStateAll = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
*data, la.LACPDUsRx = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
*data, la.MarkerPDUsRx = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
*data, la.MarkerResponsePDUsRx = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
*data, la.UnknownRx = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
*data, la.IllegalRx = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
*data, la.LACPDUsTx = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
*data, la.MarkerPDUsTx = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
*data, la.MarkerResponsePDUsTx = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
return la, nil
}
// **************************************************
// Processor Counter Record
// **************************************************
@ -2185,3 +2328,144 @@ func decodeProcessorCounters(data *[]byte) (SFlowProcessorCounters, error) {
return pc, nil
}
// SFlowEthernetFrameFlowRecord give additional information
// about the sampled packet if it's available.
// An agent may or may not provide this information.
type SFlowEthernetFrameFlowRecord struct {
SFlowBaseFlowRecord
FrameLength uint32
SrcMac net.HardwareAddr
DstMac net.HardwareAddr
Type uint32
}
// Ethernet frame flow records have the following structure:
// 0 15 31
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
// | 20 bit Interprise (0) |12 bit format |
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
// | record length |
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
// | Source Mac Address |
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
// | Destination Mac Address |
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
// | Ethernet Packet Type |
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
func decodeEthernetFrameFlowRecord(data *[]byte) (SFlowEthernetFrameFlowRecord, error) {
es := SFlowEthernetFrameFlowRecord{}
var fdf SFlowFlowDataFormat
*data, fdf = (*data)[4:], SFlowFlowDataFormat(binary.BigEndian.Uint32((*data)[:4]))
es.EnterpriseID, es.Format = fdf.decode()
*data, es.FlowDataLength = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
*data, es.FrameLength = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
*data, es.SrcMac = (*data)[8:], net.HardwareAddr((*data)[:6])
*data, es.DstMac = (*data)[8:], net.HardwareAddr((*data)[:6])
*data, es.Type = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
return es, nil
}
//SFlowOpenflowPortCounters : OVS-Sflow OpenFlow Port Counter ( 20 Bytes )
type SFlowOpenflowPortCounters struct {
SFlowBaseCounterRecord
DatapathID uint64
PortNo uint32
}
func decodeOpenflowportCounters(data *[]byte) (SFlowOpenflowPortCounters, error) {
ofp := SFlowOpenflowPortCounters{}
var cdf SFlowCounterDataFormat
*data, cdf = (*data)[4:], SFlowCounterDataFormat(binary.BigEndian.Uint32((*data)[:4]))
ofp.EnterpriseID, ofp.Format = cdf.decode()
*data, ofp.FlowDataLength = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
*data, ofp.DatapathID = (*data)[8:], binary.BigEndian.Uint64((*data)[:8])
*data, ofp.PortNo = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
return ofp, nil
}
//SFlowAppresourcesCounters : OVS_Sflow App Resources Counter ( 48 Bytes )
type SFlowAppresourcesCounters struct {
SFlowBaseCounterRecord
UserTime uint32
SystemTime uint32
MemUsed uint64
MemMax uint64
FdOpen uint32
FdMax uint32
ConnOpen uint32
ConnMax uint32
}
func decodeAppresourcesCounters(data *[]byte) (SFlowAppresourcesCounters, error) {
app := SFlowAppresourcesCounters{}
var cdf SFlowCounterDataFormat
*data, cdf = (*data)[4:], SFlowCounterDataFormat(binary.BigEndian.Uint32((*data)[:4]))
app.EnterpriseID, app.Format = cdf.decode()
*data, app.FlowDataLength = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
*data, app.UserTime = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
*data, app.SystemTime = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
*data, app.MemUsed = (*data)[8:], binary.BigEndian.Uint64((*data)[:8])
*data, app.MemMax = (*data)[8:], binary.BigEndian.Uint64((*data)[:8])
*data, app.FdOpen = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
*data, app.FdMax = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
*data, app.ConnOpen = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
*data, app.ConnMax = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
return app, nil
}
//SFlowOVSDPCounters : OVS-Sflow DataPath Counter ( 32 Bytes )
type SFlowOVSDPCounters struct {
SFlowBaseCounterRecord
NHit uint32
NMissed uint32
NLost uint32
NMaskHit uint32
NFlows uint32
NMasks uint32
}
func decodeOVSDPCounters(data *[]byte) (SFlowOVSDPCounters, error) {
dp := SFlowOVSDPCounters{}
var cdf SFlowCounterDataFormat
*data, cdf = (*data)[4:], SFlowCounterDataFormat(binary.BigEndian.Uint32((*data)[:4]))
dp.EnterpriseID, dp.Format = cdf.decode()
*data, dp.FlowDataLength = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
*data, dp.NHit = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
*data, dp.NMissed = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
*data, dp.NLost = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
*data, dp.NMaskHit = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
*data, dp.NFlows = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
*data, dp.NMasks = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
return dp, nil
}
//SFlowPORTNAME : OVS-Sflow PORTNAME Counter Sampletype ( 20 Bytes )
type SFlowPORTNAME struct {
SFlowBaseCounterRecord
Len uint32
Str string
}
func decodePortnameCounters(data *[]byte) (SFlowPORTNAME, error) {
pn := SFlowPORTNAME{}
var cdf SFlowCounterDataFormat
*data, cdf = (*data)[4:], SFlowCounterDataFormat(binary.BigEndian.Uint32((*data)[:4]))
pn.EnterpriseID, pn.Format = cdf.decode()
*data, pn.FlowDataLength = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
*data, pn.Len = (*data)[4:], binary.BigEndian.Uint32((*data)[:4])
*data, pn.Str = (*data)[8:], string(binary.BigEndian.Uint64((*data)[:8]))
return pn, nil
}

@ -189,6 +189,9 @@ type SIP struct {
Method SIPMethod
Headers map[string][]string
// Request
RequestURI string
// Response
IsResponse bool
ResponseCode int
@ -341,6 +344,8 @@ func (s *SIP) ParseFirstLine(firstLine []byte) error {
return err
}
s.RequestURI = splits[1]
// Validate SIP Version
s.Version, err = GetSIPVersion(splits[2])
if err != nil {

@ -170,7 +170,7 @@ func (t *TCP) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOpt
}
bytes[start+1] = o.OptionLength
copy(bytes[start+2:start+len(o.OptionData)+2], o.OptionData)
start += int(o.OptionLength)
start += len(o.OptionData) + 2
}
}
copy(bytes[start:], t.Padding)
@ -225,6 +225,10 @@ func (t *TCP) flagsAndOffset() uint16 {
}
func (tcp *TCP) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
if len(data) < 20 {
df.SetTruncated()
return fmt.Errorf("Invalid TCP header. Length %d less than 20", len(data))
}
tcp.SrcPort = TCPPort(binary.BigEndian.Uint16(data[0:2]))
tcp.sPort = data[0:2]
tcp.DstPort = TCPPort(binary.BigEndian.Uint16(data[2:4]))
@ -275,10 +279,15 @@ func (tcp *TCP) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
case TCPOptionKindNop: // 1 byte padding
opt.OptionLength = 1
default:
if len(data) < 2 {
df.SetTruncated()
return fmt.Errorf("Invalid TCP option length. Length %d less than 2", len(data))
}
opt.OptionLength = data[1]
if opt.OptionLength < 2 {
return fmt.Errorf("Invalid TCP option length %d < 2", opt.OptionLength)
} else if int(opt.OptionLength) > len(data) {
df.SetTruncated()
return fmt.Errorf("Invalid TCP option length %d exceeds remaining %d bytes", opt.OptionLength, len(data))
}
opt.OptionData = data[2:opt.OptionLength]

@ -28,6 +28,10 @@ type UDP struct {
func (u *UDP) LayerType() gopacket.LayerType { return LayerTypeUDP }
func (udp *UDP) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
if len(data) < 8 {
df.SetTruncated()
return fmt.Errorf("Invalid UDP header. Length %d less than 8", len(data))
}
udp.SrcPort = UDPPort(binary.BigEndian.Uint16(data[0:2]))
udp.sPort = data[0:2]
udp.DstPort = UDPPort(binary.BigEndian.Uint16(data[2:4]))

@ -16,6 +16,7 @@ import (
"reflect"
"runtime/debug"
"strings"
"syscall"
"time"
)
@ -814,7 +815,7 @@ func (p *PacketSource) packetsToChannel() {
defer close(p.c)
for {
packet, err := p.NextPacket()
if err == io.EOF {
if err == io.EOF || err == syscall.EBADF {
return
} else if err == nil {
p.c <- packet

@ -0,0 +1,74 @@
// 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.
// This file contains necessary structs/constants generated from libpcap headers with cgo -godefs
// generated with: generate_defs.exe
// DO NOT MODIFY
package pcap
import "syscall"
const errorBufferSize = 0x100
const (
pcapErrorNotActivated = -0x3
pcapErrorActivated = -0x4
pcapWarningPromisc = 0x2
pcapErrorNoSuchDevice = -0x5
pcapErrorDenied = -0x8
pcapErrorNotUp = -0x9
pcapError = -0x1
pcapWarning = 0x1
pcapDIN = 0x1
pcapDOUT = 0x2
pcapDINOUT = 0x0
pcapNetmaskUnknown = 0xffffffff
pcapTstampPrecisionMicro = 0x0
pcapTstampPrecisionNano = 0x1
)
type timeval struct {
Sec int32
Usec int32
}
type pcapPkthdr struct {
Ts timeval
Caplen uint32
Len uint32
}
type pcapTPtr uintptr
type pcapBpfInstruction struct {
Code uint16
Jt uint8
Jf uint8
K uint32
}
type pcapBpfProgram struct {
Len uint32
Insns *pcapBpfInstruction
}
type pcapStats struct {
Recv uint32
Drop uint32
Ifdrop uint32
}
type pcapCint int32
type pcapIf struct {
Next *pcapIf
Name *int8
Description *int8
Addresses *pcapAddr
Flags uint32
}
type pcapAddr struct {
Next *pcapAddr
Addr *syscall.RawSockaddr
Netmask *syscall.RawSockaddr
Broadaddr *syscall.RawSockaddr
Dstaddr *syscall.RawSockaddr
}

@ -0,0 +1,76 @@
// 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.
// This file contains necessary structs/constants generated from libpcap headers with cgo -godefs
// generated with: generate_defs.exe
// DO NOT MODIFY
package pcap
import "syscall"
const errorBufferSize = 0x100
const (
pcapErrorNotActivated = -0x3
pcapErrorActivated = -0x4
pcapWarningPromisc = 0x2
pcapErrorNoSuchDevice = -0x5
pcapErrorDenied = -0x8
pcapErrorNotUp = -0x9
pcapError = -0x1
pcapWarning = 0x1
pcapDIN = 0x1
pcapDOUT = 0x2
pcapDINOUT = 0x0
pcapNetmaskUnknown = 0xffffffff
pcapTstampPrecisionMicro = 0x0
pcapTstampPrecisionNano = 0x1
)
type timeval struct {
Sec int32
Usec int32
}
type pcapPkthdr struct {
Ts timeval
Caplen uint32
Len uint32
}
type pcapTPtr uintptr
type pcapBpfInstruction struct {
Code uint16
Jt uint8
Jf uint8
K uint32
}
type pcapBpfProgram struct {
Len uint32
Pad_cgo_0 [4]byte
Insns *pcapBpfInstruction
}
type pcapStats struct {
Recv uint32
Drop uint32
Ifdrop uint32
}
type pcapCint int32
type pcapIf struct {
Next *pcapIf
Name *int8
Description *int8
Addresses *pcapAddr
Flags uint32
Pad_cgo_0 [4]byte
}
type pcapAddr struct {
Next *pcapAddr
Addr *syscall.RawSockaddr
Netmask *syscall.RawSockaddr
Broadaddr *syscall.RawSockaddr
Dstaddr *syscall.RawSockaddr
}

@ -12,6 +12,12 @@ This package is meant to be used with its parent,
http://github.com/google/gopacket, although it can also be used independently
if you just want to get packet data from the wire.
Depending on libpcap version, os support, or file timestamp resolution,
nanosecond resolution is used for the internal timestamps. Returned timestamps
are always scaled to nanosecond resolution due to the usage of time.Time.
libpcap must be at least version 1.5 to support nanosecond timestamps. OpenLive
supports only microsecond resolution.
Reading PCAP Files
The following code can be used to read in data from a pcap file.
@ -28,7 +34,7 @@ The following code can be used to read in data from a pcap file.
Reading Live Packets
The following code can be used to read in data from a live device, in this case
"eth0".
"eth0". Be aware, that OpenLive only supports microsecond resolution.
if handle, err := pcap.OpenLive("eth0", 1600, true, pcap.BlockForever); err != nil {
panic(err)
@ -97,10 +103,10 @@ PCAP File Writing
This package does not implement PCAP file writing. However, gopacket/pcapgo
does! Look there if you'd like to write PCAP files.
Note For Windows 10 Users
Note For Windows Users
If you're trying to use 64-bit winpcap on Windows 10, you might have to do
the crazy hijinks detailed at
http://stackoverflow.com/questions/38047858/compile-gopacket-on-windows-64bit
gopacket can use winpcap or npcap. If both are installed at the same time,
npcap is preferred. Make sure the right windows service is loaded (npcap for npcap
and npf for winpcap).
*/
package pcap

157
vendor/github.com/google/gopacket/pcap/generate_defs.go generated vendored Normal file

@ -0,0 +1,157 @@
// 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.
// +build ignore
package main
// This file generates the godefs needed for the windows version.
// Rebuild is only necessary if additional libpcap functionality is implemented, or a new arch is implemented in golang.
// Call with go run generate_windows.go [-I includepath]
// Needs npcap sdk, go tool cgo, and gofmt to work. Location of npcap includes can be specified with -I
import (
"bytes"
"flag"
"fmt"
"io/ioutil"
"log"
"os"
"os/exec"
"path/filepath"
"strings"
)
const header = `// 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.
// This file contains necessary structs/constants generated from libpcap headers with cgo -godefs
// generated with: %s
// DO NOT MODIFY
`
const source = `
package pcap
//#include <pcap.h>
import "C"
import "syscall" // needed for RawSockaddr
const errorBufferSize = C.PCAP_ERRBUF_SIZE
const (
pcapErrorNotActivated = C.PCAP_ERROR_NOT_ACTIVATED
pcapErrorActivated = C.PCAP_ERROR_ACTIVATED
pcapWarningPromisc = C.PCAP_WARNING_PROMISC_NOTSUP
pcapErrorNoSuchDevice = C.PCAP_ERROR_NO_SUCH_DEVICE
pcapErrorDenied = C.PCAP_ERROR_PERM_DENIED
pcapErrorNotUp = C.PCAP_ERROR_IFACE_NOT_UP
pcapError = C.PCAP_ERROR
pcapWarning = C.PCAP_WARNING
pcapDIN = C.PCAP_D_IN
pcapDOUT = C.PCAP_D_OUT
pcapDINOUT = C.PCAP_D_INOUT
pcapNetmaskUnknown = C.PCAP_NETMASK_UNKNOWN
pcapTstampPrecisionMicro = C.PCAP_TSTAMP_PRECISION_MICRO
pcapTstampPrecisionNano = C.PCAP_TSTAMP_PRECISION_NANO
)
type timeval C.struct_timeval
type pcapPkthdr C.struct_pcap_pkthdr
type pcapTPtr uintptr
type pcapBpfInstruction C.struct_bpf_insn
type pcapBpfProgram C.struct_bpf_program
type pcapStats C.struct_pcap_stat
type pcapCint C.int
type pcapIf C.struct_pcap_if
// +godefs map struct_sockaddr syscall.RawSockaddr
type pcapAddr C.struct_pcap_addr
`
var includes = flag.String("I", "C:\\npcap-sdk-1.01\\Include", "Include path containing libpcap headers")
func main() {
flag.Parse()
infile, err := ioutil.TempFile(".", "defs.*.go")
if err != nil {
log.Fatal("Couldn't create temporary source file: ", err)
}
defer infile.Close()
defer os.Remove(infile.Name())
_, err = infile.WriteString(source)
if err != nil {
log.Fatalf("Couldn't write definitions to temporary file %s: %s", infile.Name(), err)
}
err = infile.Close()
if err != nil {
log.Fatalf("Couldn't close temporary source file %s: %s", infile.Name(), err)
}
archs := []string{"386", "amd64"}
for _, arch := range archs {
env := append(os.Environ(), "GOARCH="+arch)
cmd := exec.Command("go", "tool", "cgo", "-godefs", "--", "-I", *includes, infile.Name())
cmd.Env = env
cmd.Stderr = os.Stderr
var generated bytes.Buffer
cmd.Stdout = &generated
err := cmd.Run()
if err != nil {
log.Fatalf("Couldn't generated defs for %s: %s\n", arch, err)
}
cmd = exec.Command("gofmt")
cmd.Env = env
cmd.Stderr = os.Stderr
outName := fmt.Sprintf("defs_windows_%s.go", arch)
out, err := os.Create(outName)
if err != nil {
log.Fatalf("Couldn't open file %s: %s", outName, err)
}
cmd.Stdout = out
in, err := cmd.StdinPipe()
if err != nil {
log.Fatal("Couldn't create input pipe for gofmt: ", err)
}
err = cmd.Start()
if err != nil {
log.Fatal("Couldn't start gofmt: ", err)
}
_, err = fmt.Fprintf(in, header, strings.Join(append([]string{filepath.Base(os.Args[0])}, os.Args[1:]...), " "))
if err != nil {
log.Fatal("Couldn't write header to gofmt: ", err)
}
for {
line, err := generated.ReadBytes('\n')
if err != nil {
break
}
// remove godefs comments
if bytes.HasPrefix(line, []byte("//")) {
continue
}
_, err = in.Write(line)
if err != nil {
log.Fatal("Couldn't write line to gofmt: ", err)
}
}
in.Close()
err = cmd.Wait()
if err != nil {
log.Fatal("gofmt failed: ", err)
}
out.Close()
}
}

File diff suppressed because it is too large Load Diff

@ -15,7 +15,6 @@ import (
"flag"
"fmt"
"log"
"net"
"net/http"
"os"
"time"
@ -35,7 +34,7 @@ func generatePackets() {
func main() {
flag.Parse()
ifaces, err := net.Interfaces()
ifaces, err := pcap.FindAllDevs()
if err != nil {
log.Fatal(err)
}
@ -51,7 +50,7 @@ func main() {
os.Exit(1)
}
func tryCapture(iface net.Interface) error {
func tryCapture(iface pcap.Interface) error {
if iface.Name[:2] == "lo" {
return errors.New("skipping loopback")
}

@ -9,9 +9,131 @@
package pcap
import (
"errors"
"os"
"sync"
"syscall"
"time"
"unsafe"
"github.com/google/gopacket"
"github.com/google/gopacket/layers"
)
/*
#cgo solaris LDFLAGS: -L /opt/local/lib -lpcap
#cgo linux LDFLAGS: -lpcap
#cgo dragonfly LDFLAGS: -lpcap
#cgo freebsd LDFLAGS: -lpcap
#cgo openbsd LDFLAGS: -lpcap
#cgo netbsd LDFLAGS: -lpcap
#cgo darwin LDFLAGS: -lpcap
#include <stdlib.h>
#include <pcap.h>
#include <stdint.h>
// Some old versions of pcap don't define this constant.
#ifndef PCAP_NETMASK_UNKNOWN
#define PCAP_NETMASK_UNKNOWN 0xffffffff
#endif
// libpcap doesn't actually export its version in a #define-guardable way,
// so we have to use other defined things to differentiate versions.
// We assume at least libpcap v1.1 at the moment.
// See http://upstream-tracker.org/versions/libpcap.html
#ifndef PCAP_ERROR_TSTAMP_PRECISION_NOTSUP // < v1.5
#define PCAP_ERROR_TSTAMP_PRECISION_NOTSUP -12
int pcap_set_immediate_mode(pcap_t *p, int mode) {
return PCAP_ERROR;
}
// libpcap version < v1.5 doesn't have timestamp precision (everything is microsecond)
//
// This means *_tstamp_* functions and macros are missing. Therefore, we emulate these
// functions here and pretend the setting the precision works. This is actually the way
// the pcap_open_offline_with_tstamp_precision works, because it doesn't return an error
// if it was not possible to set the precision, which depends on support by the given file.
// => The rest of the functions always pretend as if they could set nano precision and
// verify the actual precision with pcap_get_tstamp_precision, which is emulated for <v1.5
// to always return micro resolution.
#define PCAP_TSTAMP_PRECISION_MICRO 0
#define PCAP_TSTAMP_PRECISION_NANO 1
pcap_t *pcap_open_offline_with_tstamp_precision(const char *fname, u_int precision,
char *errbuf) {
return pcap_open_offline(fname, errbuf);
}
int pcap_set_tstamp_precision(pcap_t *p, int tstamp_precision) {
if (tstamp_precision == PCAP_TSTAMP_PRECISION_MICRO)
return 0;
return PCAP_ERROR_TSTAMP_PRECISION_NOTSUP;
}
int pcap_get_tstamp_precision(pcap_t *p) {
return PCAP_TSTAMP_PRECISION_MICRO
}
#ifndef PCAP_TSTAMP_HOST // < v1.2
int pcap_set_tstamp_type(pcap_t* p, int t) { return -1; }
int pcap_list_tstamp_types(pcap_t* p, int** t) { return 0; }
void pcap_free_tstamp_types(int *tstamp_types) {}
const char* pcap_tstamp_type_val_to_name(int t) {
return "pcap timestamp types not supported";
}
int pcap_tstamp_type_name_to_val(const char* t) {
return PCAP_ERROR;
}
#endif // < v1.2
#endif // < v1.5
#ifndef PCAP_ERROR_PROMISC_PERM_DENIED
#define PCAP_ERROR_PROMISC_PERM_DENIED -11
#endif
// Windows, Macs, and Linux all use different time types. Joy.
#ifdef __APPLE__
#define gopacket_time_secs_t __darwin_time_t
#define gopacket_time_usecs_t __darwin_suseconds_t
#elif __ANDROID__
#define gopacket_time_secs_t __kernel_time_t
#define gopacket_time_usecs_t __kernel_suseconds_t
#elif __GLIBC__
#define gopacket_time_secs_t __time_t
#define gopacket_time_usecs_t __suseconds_t
#else // Some form of linux/bsd/etc...
#include <sys/param.h>
#ifdef __OpenBSD__
#define gopacket_time_secs_t u_int32_t
#define gopacket_time_usecs_t u_int32_t
#else
#define gopacket_time_secs_t time_t
#define gopacket_time_usecs_t suseconds_t
#endif
#endif
// The things we do to avoid pointers escaping to the heap...
// According to https://github.com/the-tcpdump-group/libpcap/blob/1131a7c26c6f4d4772e4a2beeaf7212f4dea74ac/pcap.c#L398-L406 ,
// the return value of pcap_next_ex could be greater than 1 for success.
// Let's just make it 1 if it comes bigger than 1.
int pcap_next_ex_escaping(pcap_t *p, uintptr_t pkt_hdr, uintptr_t pkt_data) {
int ex = pcap_next_ex(p, (struct pcap_pkthdr**)(pkt_hdr), (const u_char**)(pkt_data));
if (ex > 1) {
ex = 1;
}
return ex;
}
int pcap_offline_filter_escaping(struct bpf_program *fp, uintptr_t pkt_hdr, uintptr_t pkt) {
return pcap_offline_filter(fp, (struct pcap_pkthdr*)(pkt_hdr), (const u_char*)(pkt));
}
// pcap_wait returns when the next packet is available or the timeout expires.
// Since it uses pcap_get_selectable_fd, it will not work in Windows.
@ -38,22 +160,526 @@ int pcap_wait(pcap_t *p, int usec) {
// block indefinitely if no timeout provided
return select(fd+1, &fds, NULL, NULL, NULL);
}
// libpcap version < v1.5 doesn't have timestamp precision (everything is microsecond)
// see pcap.go for an explanation of why precision is ignored
#ifndef PCAP_ERROR_TSTAMP_PRECISION_NOTSUP // < v1.5
pcap_t *pcap_fopen_offline_with_tstamp_precision(FILE *fp, u_int precision,
char *errbuf) {
return pcap_fopen_offline(fp, errbuf);
}
#endif // < v1.5
*/
import "C"
import (
"errors"
"os"
"unsafe"
const errorBufferSize = C.PCAP_ERRBUF_SIZE
const (
pcapErrorNotActivated = C.PCAP_ERROR_NOT_ACTIVATED
pcapErrorActivated = C.PCAP_ERROR_ACTIVATED
pcapWarningPromisc = C.PCAP_WARNING_PROMISC_NOTSUP
pcapErrorNoSuchDevice = C.PCAP_ERROR_NO_SUCH_DEVICE
pcapErrorDenied = C.PCAP_ERROR_PERM_DENIED
pcapErrorNotUp = C.PCAP_ERROR_IFACE_NOT_UP
pcapWarning = C.PCAP_WARNING
pcapDIN = C.PCAP_D_IN
pcapDOUT = C.PCAP_D_OUT
pcapDINOUT = C.PCAP_D_INOUT
pcapNetmaskUnknown = C.PCAP_NETMASK_UNKNOWN
pcapTstampPrecisionMicro = C.PCAP_TSTAMP_PRECISION_MICRO
pcapTstampPrecisionNano = C.PCAP_TSTAMP_PRECISION_NANO
)
type pcapPkthdr C.struct_pcap_pkthdr
type pcapTPtr *C.struct_pcap
type pcapBpfProgram C.struct_bpf_program
func (h *pcapPkthdr) getSec() int64 {
return int64(h.ts.tv_sec)
}
func (h *pcapPkthdr) getUsec() int64 {
return int64(h.ts.tv_usec)
}
func (h *pcapPkthdr) getLen() int {
return int(h.len)
}
func (h *pcapPkthdr) getCaplen() int {
return int(h.caplen)
}
func pcapGetTstampPrecision(cptr pcapTPtr) int {
return int(C.pcap_get_tstamp_precision(cptr))
}
func pcapSetTstampPrecision(cptr pcapTPtr, precision int) error {
ret := C.pcap_set_tstamp_precision(cptr, C.int(precision))
if ret < 0 {
return errors.New(C.GoString(C.pcap_geterr(cptr)))
}
return nil
}
func statusError(status C.int) error {
return errors.New(C.GoString(C.pcap_statustostr(status)))
}
func pcapOpenLive(device string, snaplen int, pro int, timeout int) (*Handle, error) {
buf := (*C.char)(C.calloc(errorBufferSize, 1))
defer C.free(unsafe.Pointer(buf))
dev := C.CString(device)
defer C.free(unsafe.Pointer(dev))
cptr := C.pcap_open_live(dev, C.int(snaplen), C.int(pro), C.int(timeout), buf)
if cptr == nil {
return nil, errors.New(C.GoString(buf))
}
return &Handle{cptr: cptr}, nil
}
func openOffline(file string) (handle *Handle, err error) {
buf := (*C.char)(C.calloc(errorBufferSize, 1))
defer C.free(unsafe.Pointer(buf))
cf := C.CString(file)
defer C.free(unsafe.Pointer(cf))
cptr := C.pcap_open_offline_with_tstamp_precision(cf, C.PCAP_TSTAMP_PRECISION_NANO, buf)
if cptr == nil {
return nil, errors.New(C.GoString(buf))
}
return &Handle{cptr: cptr}, nil
}
func (p *Handle) pcapClose() {
if p.cptr != nil {
C.pcap_close(p.cptr)
}
p.cptr = nil
}
func (p *Handle) pcapGeterr() error {
return errors.New(C.GoString(C.pcap_geterr(p.cptr)))
}
func (p *Handle) pcapStats() (*Stats, error) {
var cstats C.struct_pcap_stat
if C.pcap_stats(p.cptr, &cstats) < 0 {
return nil, p.pcapGeterr()
}
return &Stats{
PacketsReceived: int(cstats.ps_recv),
PacketsDropped: int(cstats.ps_drop),
PacketsIfDropped: int(cstats.ps_ifdrop),
}, nil
}
// for libpcap < 1.8 pcap_compile is NOT thread-safe, so protect it.
var pcapCompileMu sync.Mutex
func (p *Handle) pcapCompile(expr string, maskp uint32) (pcapBpfProgram, error) {
var bpf pcapBpfProgram
cexpr := C.CString(expr)
defer C.free(unsafe.Pointer(cexpr))
pcapCompileMu.Lock()
defer pcapCompileMu.Unlock()
if C.pcap_compile(p.cptr, (*C.struct_bpf_program)(&bpf), cexpr, 1, C.bpf_u_int32(maskp)) < 0 {
return bpf, p.pcapGeterr()
}
return bpf, nil
}
func (p pcapBpfProgram) free() {
C.pcap_freecode((*C.struct_bpf_program)(&p))
}
func (p pcapBpfProgram) toBPFInstruction() []BPFInstruction {
bpfInsn := (*[bpfInstructionBufferSize]C.struct_bpf_insn)(unsafe.Pointer(p.bf_insns))[0:p.bf_len:p.bf_len]
bpfInstruction := make([]BPFInstruction, len(bpfInsn), len(bpfInsn))
for i, v := range bpfInsn {
bpfInstruction[i].Code = uint16(v.code)
bpfInstruction[i].Jt = uint8(v.jt)
bpfInstruction[i].Jf = uint8(v.jf)
bpfInstruction[i].K = uint32(v.k)
}
return bpfInstruction
}
func pcapBpfProgramFromInstructions(bpfInstructions []BPFInstruction) pcapBpfProgram {
var bpf pcapBpfProgram
bpf.bf_len = C.u_int(len(bpfInstructions))
cbpfInsns := C.calloc(C.size_t(len(bpfInstructions)), C.size_t(unsafe.Sizeof(bpfInstructions[0])))
gbpfInsns := (*[bpfInstructionBufferSize]C.struct_bpf_insn)(cbpfInsns)
for i, v := range bpfInstructions {
gbpfInsns[i].code = C.u_short(v.Code)
gbpfInsns[i].jt = C.u_char(v.Jt)
gbpfInsns[i].jf = C.u_char(v.Jf)
gbpfInsns[i].k = C.bpf_u_int32(v.K)
}
bpf.bf_insns = (*C.struct_bpf_insn)(cbpfInsns)
return bpf
}
func pcapLookupnet(device string) (netp, maskp uint32, err error) {
errorBuf := (*C.char)(C.calloc(errorBufferSize, 1))
defer C.free(unsafe.Pointer(errorBuf))
dev := C.CString(device)
defer C.free(unsafe.Pointer(dev))
if C.pcap_lookupnet(
dev,
(*C.bpf_u_int32)(unsafe.Pointer(&netp)),
(*C.bpf_u_int32)(unsafe.Pointer(&maskp)),
errorBuf,
) < 0 {
return 0, 0, errors.New(C.GoString(errorBuf))
// We can't lookup the network, but that could be because the interface
// doesn't have an IPv4.
}
return
}
func (b *BPF) pcapOfflineFilter(ci gopacket.CaptureInfo, data []byte) bool {
hdr := (*C.struct_pcap_pkthdr)(&b.hdr)
hdr.ts.tv_sec = C.gopacket_time_secs_t(ci.Timestamp.Unix())
hdr.ts.tv_usec = C.gopacket_time_usecs_t(ci.Timestamp.Nanosecond() / 1000)
hdr.caplen = C.bpf_u_int32(len(data)) // Trust actual length over ci.Length.
hdr.len = C.bpf_u_int32(ci.Length)
dataptr := (*C.u_char)(unsafe.Pointer(&data[0]))
return C.pcap_offline_filter_escaping((*C.struct_bpf_program)(&b.bpf),
C.uintptr_t(uintptr(unsafe.Pointer(hdr))),
C.uintptr_t(uintptr(unsafe.Pointer(dataptr)))) != 0
}
func (p *Handle) pcapSetfilter(bpf pcapBpfProgram) error {
if C.pcap_setfilter(p.cptr, (*C.struct_bpf_program)(&bpf)) < 0 {
return p.pcapGeterr()
}
return nil
}
func (p *Handle) pcapListDatalinks() (datalinks []Datalink, err error) {
var dltbuf *C.int
n := int(C.pcap_list_datalinks(p.cptr, &dltbuf))
if n < 0 {
return nil, p.pcapGeterr()
}
defer C.pcap_free_datalinks(dltbuf)
datalinks = make([]Datalink, n)
dltArray := (*[1 << 28]C.int)(unsafe.Pointer(dltbuf))
for i := 0; i < n; i++ {
datalinks[i].Name = pcapDatalinkValToName(int((*dltArray)[i]))
datalinks[i].Description = pcapDatalinkValToDescription(int((*dltArray)[i]))
}
return datalinks, nil
}
func pcapOpenDead(linkType layers.LinkType, captureLength int) (*Handle, error) {
cptr := C.pcap_open_dead(C.int(linkType), C.int(captureLength))
if cptr == nil {
return nil, errors.New("error opening dead capture")
}
return &Handle{cptr: cptr}, nil
}
func (p *Handle) pcapNextPacketEx() NextError {
// This horrible magic allows us to pass a ptr-to-ptr to pcap_next_ex
// without causing that ptr-to-ptr to itself be allocated on the heap.
// Since Handle itself survives through the duration of the pcap_next_ex
// call, this should be perfectly safe for GC stuff, etc.
return NextError(C.pcap_next_ex_escaping(p.cptr, C.uintptr_t(uintptr(unsafe.Pointer(&p.pkthdr))), C.uintptr_t(uintptr(unsafe.Pointer(&p.bufptr)))))
}
func (p *Handle) pcapDatalink() layers.LinkType {
return layers.LinkType(C.pcap_datalink(p.cptr))
}
func (p *Handle) pcapSetDatalink(dlt layers.LinkType) error {
if C.pcap_set_datalink(p.cptr, C.int(dlt)) < 0 {
return p.pcapGeterr()
}
return nil
}
func pcapDatalinkValToName(dlt int) string {
return C.GoString(C.pcap_datalink_val_to_name(C.int(dlt)))
}
func pcapDatalinkValToDescription(dlt int) string {
return C.GoString(C.pcap_datalink_val_to_description(C.int(dlt)))
}
func pcapDatalinkNameToVal(name string) int {
cptr := C.CString(name)
defer C.free(unsafe.Pointer(cptr))
return int(C.pcap_datalink_name_to_val(cptr))
}
func pcapLibVersion() string {
return C.GoString(C.pcap_lib_version())
}
func (p *Handle) isOpen() bool {
return p.cptr != nil
}
type pcapDevices struct {
all, cur *C.pcap_if_t
}
func (p pcapDevices) free() {
C.pcap_freealldevs((*C.pcap_if_t)(p.all))
}
func (p *pcapDevices) next() bool {
if p.cur == nil {
p.cur = p.all
if p.cur == nil {
return false
}
return true
}
if p.cur.next == nil {
return false
}
p.cur = p.cur.next
return true
}
func (p pcapDevices) name() string {
return C.GoString(p.cur.name)
}
func (p pcapDevices) description() string {
return C.GoString(p.cur.description)
}
func (p pcapDevices) flags() uint32 {
return uint32(p.cur.flags)
}
type pcapAddresses struct {
all, cur *C.pcap_addr_t
}
func (p *pcapAddresses) next() bool {
if p.cur == nil {
p.cur = p.all
if p.cur == nil {
return false
}
return true
}
if p.cur.next == nil {
return false
}
p.cur = p.cur.next
return true
}
func (p pcapAddresses) addr() *syscall.RawSockaddr {
return (*syscall.RawSockaddr)(unsafe.Pointer(p.cur.addr))
}
func (p pcapAddresses) netmask() *syscall.RawSockaddr {
return (*syscall.RawSockaddr)(unsafe.Pointer(p.cur.netmask))
}
func (p pcapAddresses) broadaddr() *syscall.RawSockaddr {
return (*syscall.RawSockaddr)(unsafe.Pointer(p.cur.broadaddr))
}
func (p pcapAddresses) dstaddr() *syscall.RawSockaddr {
return (*syscall.RawSockaddr)(unsafe.Pointer(p.cur.dstaddr))
}
func (p pcapDevices) addresses() pcapAddresses {
return pcapAddresses{all: p.cur.addresses}
}
func pcapFindAllDevs() (pcapDevices, error) {
var buf *C.char
buf = (*C.char)(C.calloc(errorBufferSize, 1))
defer C.free(unsafe.Pointer(buf))
var alldevsp pcapDevices
if C.pcap_findalldevs((**C.pcap_if_t)(&alldevsp.all), buf) < 0 {
return pcapDevices{}, errors.New(C.GoString(buf))
}
return alldevsp, nil
}
func (p *Handle) pcapSendpacket(data []byte) error {
if C.pcap_sendpacket(p.cptr, (*C.u_char)(&data[0]), (C.int)(len(data))) < 0 {
return p.pcapGeterr()
}
return nil
}
func (p *Handle) pcapSetdirection(direction Direction) error {
if status := C.pcap_setdirection(p.cptr, (C.pcap_direction_t)(direction)); status < 0 {
return statusError(status)
}
return nil
}
func (p *Handle) pcapSnapshot() int {
return int(C.pcap_snapshot(p.cptr))
}
func (t TimestampSource) pcapTstampTypeValToName() string {
return C.GoString(C.pcap_tstamp_type_val_to_name(C.int(t)))
}
func pcapTstampTypeNameToVal(s string) (TimestampSource, error) {
cs := C.CString(s)
defer C.free(unsafe.Pointer(cs))
t := C.pcap_tstamp_type_name_to_val(cs)
if t < 0 {
return 0, statusError(t)
}
return TimestampSource(t), nil
}
func (p *InactiveHandle) pcapGeterr() error {
return errors.New(C.GoString(C.pcap_geterr(p.cptr)))
}
func (p *InactiveHandle) pcapActivate() (*Handle, activateError) {
ret := activateError(C.pcap_activate(p.cptr))
if ret != aeNoError {
return nil, ret
}
h := &Handle{
cptr: p.cptr,
}
p.cptr = nil
return h, ret
}
func (p *InactiveHandle) pcapClose() {
if p.cptr != nil {
C.pcap_close(p.cptr)
}
}
func pcapCreate(device string) (*InactiveHandle, error) {
buf := (*C.char)(C.calloc(errorBufferSize, 1))
defer C.free(unsafe.Pointer(buf))
dev := C.CString(device)
defer C.free(unsafe.Pointer(dev))
cptr := C.pcap_create(dev, buf)
if cptr == nil {
return nil, errors.New(C.GoString(buf))
}
return &InactiveHandle{cptr: cptr}, nil
}
func (p *InactiveHandle) pcapSetSnaplen(snaplen int) error {
if status := C.pcap_set_snaplen(p.cptr, C.int(snaplen)); status < 0 {
return statusError(status)
}
return nil
}
func (p *InactiveHandle) pcapSetPromisc(promisc bool) error {
var pro C.int
if promisc {
pro = 1
}
if status := C.pcap_set_promisc(p.cptr, pro); status < 0 {
return statusError(status)
}
return nil
}
func (p *InactiveHandle) pcapSetTimeout(timeout time.Duration) error {
if status := C.pcap_set_timeout(p.cptr, C.int(timeoutMillis(timeout))); status < 0 {
return statusError(status)
}
return nil
}
func (p *InactiveHandle) pcapListTstampTypes() (out []TimestampSource) {
var types *C.int
n := int(C.pcap_list_tstamp_types(p.cptr, &types))
if n < 0 {
return // public interface doesn't have error :(
}
defer C.pcap_free_tstamp_types(types)
typesArray := (*[1 << 28]C.int)(unsafe.Pointer(types))
for i := 0; i < n; i++ {
out = append(out, TimestampSource((*typesArray)[i]))
}
return
}
func (p *InactiveHandle) pcapSetTstampType(t TimestampSource) error {
if status := C.pcap_set_tstamp_type(p.cptr, C.int(t)); status < 0 {
return statusError(status)
}
return nil
}
func (p *InactiveHandle) pcapSetRfmon(monitor bool) error {
var mon C.int
if monitor {
mon = 1
}
switch canset := C.pcap_can_set_rfmon(p.cptr); canset {
case 0:
return CannotSetRFMon
case 1:
// success
default:
return statusError(canset)
}
if status := C.pcap_set_rfmon(p.cptr, mon); status != 0 {
return statusError(status)
}
return nil
}
func (p *InactiveHandle) pcapSetBufferSize(bufferSize int) error {
if status := C.pcap_set_buffer_size(p.cptr, C.int(bufferSize)); status < 0 {
return statusError(status)
}
return nil
}
func (p *InactiveHandle) pcapSetImmediateMode(mode bool) error {
var md C.int
if mode {
md = 1
}
if status := C.pcap_set_immediate_mode(p.cptr, md); status < 0 {
return statusError(status)
}
return nil
}
func (p *Handle) setNonBlocking() error {
buf := (*C.char)(C.calloc(errorBufferSize, 1))
defer C.free(unsafe.Pointer(buf))
// Change the device to non-blocking, we'll use pcap_wait to wait until the
// handle is ready to read.
if v := C.pcap_setnonblock(p.cptr, 1, buf); v == -1 {
if v := C.pcap_setnonblock(p.cptr, 1, buf); v < -1 {
return errors.New(C.GoString(buf))
}
@ -68,7 +694,7 @@ func (p *Handle) waitForPacket() {
usec := timeoutMillis(p.timeout) * 1000
usec -= 100
C.pcap_wait(p.cptr, usec)
C.pcap_wait(p.cptr, C.int(usec))
}
// openOfflineFile returns contents of input file as a *Handle.
@ -79,7 +705,7 @@ func openOfflineFile(file *os.File) (handle *Handle, err error) {
defer C.free(unsafe.Pointer(cmode))
cf := C.fdopen(C.int(file.Fd()), cmode)
cptr := C.pcap_fopen_offline(cf, buf)
cptr := C.pcap_fopen_offline_with_tstamp_precision(cf, C.PCAP_TSTAMP_PRECISION_NANO, buf)
if cptr == nil {
return nil, errors.New(C.GoString(buf))
}

@ -7,18 +7,779 @@
package pcap
/*
#include <pcap.h>
*/
import "C"
import (
"errors"
"fmt"
"os"
"runtime"
"sync"
"syscall"
"time"
"unsafe"
"github.com/google/gopacket"
"github.com/google/gopacket/layers"
)
const npcapPath = "\\Npcap"
func initDllPath(kernel32 syscall.Handle) {
setDllDirectory, err := syscall.GetProcAddress(kernel32, "SetDllDirectoryA")
if err != nil {
// we can't do anything since SetDllDirectoryA is missing - fall back to use first wpcap.dll we encounter
return
}
getSystemDirectory, err := syscall.GetProcAddress(kernel32, "GetSystemDirectoryA")
if err != nil {
// we can't do anything since SetDllDirectoryA is missing - fall back to use first wpcap.dll we encounter
return
}
buf := make([]byte, 4096)
r, _, _ := syscall.Syscall(getSystemDirectory, 2, uintptr(unsafe.Pointer(&buf[0])), uintptr(len(buf)), 0)
if r == 0 || r > 4096-uintptr(len(npcapPath))-1 {
// we can't do anything since SetDllDirectoryA is missing - fall back to use first wpcap.dll we encounter
return
}
copy(buf[r:], npcapPath)
_, _, _ = syscall.Syscall(setDllDirectory, 1, uintptr(unsafe.Pointer(&buf[0])), 0, 0)
// ignore errors here - we just fallback to load wpcap.dll from default locations
}
// loadedDllPath will hold the full pathname of the loaded wpcap.dll after init if possible
var loadedDllPath = "wpcap.dll"
func initLoadedDllPath(kernel32 syscall.Handle) {
getModuleFileName, err := syscall.GetProcAddress(kernel32, "GetModuleFileNameA")
if err != nil {
// we can't get the filename of the loaded module in this case - just leave default of wpcap.dll
return
}
buf := make([]byte, 4096)
r, _, _ := syscall.Syscall(getModuleFileName, 3, uintptr(wpcapHandle), uintptr(unsafe.Pointer(&buf[0])), uintptr(len(buf)))
if r == 0 {
// we can't get the filename of the loaded module in this case - just leave default of wpcap.dll
return
}
loadedDllPath = string(buf[:int(r)])
}
func mustLoad(fun string) uintptr {
addr, err := syscall.GetProcAddress(wpcapHandle, fun)
if err != nil {
panic(fmt.Sprintf("Couldn't load function %s from %s", fun, loadedDllPath))
}
return addr
}
func mightLoad(fun string) uintptr {
addr, err := syscall.GetProcAddress(wpcapHandle, fun)
if err != nil {
return 0
}
return addr
}
func byteSliceToString(bval []byte) string {
for i := range bval {
if bval[i] == 0 {
return string(bval[:i])
}
}
return string(bval[:])
}
// bytePtrToString returns a string copied from pointer to a null terminated byte array
// WARNING: ONLY SAFE WITH IF r POINTS TO C MEMORY!
// govet will complain about this function for the reason stated above
func bytePtrToString(r uintptr) string {
if r == 0 {
return ""
}
bval := (*[1 << 30]byte)(unsafe.Pointer(r))
return byteSliceToString(bval[:])
}
var wpcapHandle syscall.Handle
var msvcrtHandle syscall.Handle
var (
callocPtr,
pcapStrerrorPtr,
pcapStatustostrPtr,
pcapOpenLivePtr,
pcapOpenOfflinePtr,
pcapClosePtr,
pcapGeterrPtr,
pcapStatsPtr,
pcapCompilePtr,
pcapFreecodePtr,
pcapLookupnetPtr,
pcapOfflineFilterPtr,
pcapSetfilterPtr,
pcapListDatalinksPtr,
pcapFreeDatalinksPtr,
pcapDatalinkValToNamePtr,
pcapDatalinkValToDescriptionPtr,
pcapOpenDeadPtr,
pcapNextExPtr,
pcapDatalinkPtr,
pcapSetDatalinkPtr,
pcapDatalinkNameToValPtr,
pcapLibVersionPtr,
pcapFreealldevsPtr,
pcapFindalldevsPtr,
pcapSendpacketPtr,
pcapSetdirectionPtr,
pcapSnapshotPtr,
pcapTstampTypeValToNamePtr,
pcapTstampTypeNameToValPtr,
pcapListTstampTypesPtr,
pcapFreeTstampTypesPtr,
pcapSetTstampTypePtr,
pcapGetTstampPrecisionPtr,
pcapSetTstampPrecisionPtr,
pcapOpenOfflineWithTstampPrecisionPtr,
pcapHOpenOfflineWithTstampPrecisionPtr,
pcapActivatePtr,
pcapCreatePtr,
pcapSetSnaplenPtr,
pcapSetPromiscPtr,
pcapSetTimeoutPtr,
pcapCanSetRfmonPtr,
pcapSetRfmonPtr,
pcapSetBufferSizePtr,
pcapSetImmediateModePtr,
pcapHopenOfflinePtr uintptr
)
func init() {
kernel32, err := syscall.LoadLibrary("kernel32.dll")
if err != nil {
panic("couldn't load kernel32.dll")
}
defer syscall.FreeLibrary(kernel32)
initDllPath(kernel32)
wpcapHandle, err = syscall.LoadLibrary("wpcap.dll")
if err != nil {
panic("Couldn't load wpcap.dll")
}
initLoadedDllPath(kernel32)
msvcrtHandle, err = syscall.LoadLibrary("msvcrt.dll")
if err != nil {
panic("Couldn't load msvcrt.dll")
}
callocPtr, err = syscall.GetProcAddress(msvcrtHandle, "calloc")
if err != nil {
panic("Couldn't get calloc function")
}
pcapStrerrorPtr = mustLoad("pcap_strerror")
pcapStatustostrPtr = mightLoad("pcap_statustostr") // not available on winpcap
pcapOpenLivePtr = mustLoad("pcap_open_live")
pcapOpenOfflinePtr = mustLoad("pcap_open_offline")
pcapClosePtr = mustLoad("pcap_close")
pcapGeterrPtr = mustLoad("pcap_geterr")
pcapStatsPtr = mustLoad("pcap_stats")
pcapCompilePtr = mustLoad("pcap_compile")
pcapFreecodePtr = mustLoad("pcap_freecode")
pcapLookupnetPtr = mustLoad("pcap_lookupnet")
pcapOfflineFilterPtr = mustLoad("pcap_offline_filter")
pcapSetfilterPtr = mustLoad("pcap_setfilter")
pcapListDatalinksPtr = mustLoad("pcap_list_datalinks")
pcapFreeDatalinksPtr = mustLoad("pcap_free_datalinks")
pcapDatalinkValToNamePtr = mustLoad("pcap_datalink_val_to_name")
pcapDatalinkValToDescriptionPtr = mustLoad("pcap_datalink_val_to_description")
pcapOpenDeadPtr = mustLoad("pcap_open_dead")
pcapNextExPtr = mustLoad("pcap_next_ex")
pcapDatalinkPtr = mustLoad("pcap_datalink")
pcapSetDatalinkPtr = mustLoad("pcap_set_datalink")
pcapDatalinkNameToValPtr = mustLoad("pcap_datalink_name_to_val")
pcapLibVersionPtr = mustLoad("pcap_lib_version")
pcapFreealldevsPtr = mustLoad("pcap_freealldevs")
pcapFindalldevsPtr = mustLoad("pcap_findalldevs")
pcapSendpacketPtr = mustLoad("pcap_sendpacket")
pcapSetdirectionPtr = mustLoad("pcap_setdirection")
pcapSnapshotPtr = mustLoad("pcap_snapshot")
//libpcap <1.2 doesn't have pcap_*_tstamp_* functions
pcapTstampTypeValToNamePtr = mightLoad("pcap_tstamp_type_val_to_name")
pcapTstampTypeNameToValPtr = mightLoad("pcap_tstamp_type_name_to_val")
pcapListTstampTypesPtr = mightLoad("pcap_list_tstamp_types")
pcapFreeTstampTypesPtr = mightLoad("pcap_free_tstamp_types")
pcapSetTstampTypePtr = mightLoad("pcap_set_tstamp_type")
pcapGetTstampPrecisionPtr = mightLoad("pcap_get_tstamp_precision")
pcapSetTstampPrecisionPtr = mightLoad("pcap_set_tstamp_precision")
pcapOpenOfflineWithTstampPrecisionPtr = mightLoad("pcap_open_offline_with_tstamp_precision")
pcapHOpenOfflineWithTstampPrecisionPtr = mightLoad("pcap_hopen_offline_with_tstamp_precision")
pcapActivatePtr = mustLoad("pcap_activate")
pcapCreatePtr = mustLoad("pcap_create")
pcapSetSnaplenPtr = mustLoad("pcap_set_snaplen")
pcapSetPromiscPtr = mustLoad("pcap_set_promisc")
pcapSetTimeoutPtr = mustLoad("pcap_set_timeout")
//winpcap does not support rfmon
pcapCanSetRfmonPtr = mightLoad("pcap_can_set_rfmon")
pcapSetRfmonPtr = mightLoad("pcap_set_rfmon")
pcapSetBufferSizePtr = mustLoad("pcap_set_buffer_size")
//libpcap <1.5 does not have pcap_set_immediate_mode
pcapSetImmediateModePtr = mightLoad("pcap_set_immediate_mode")
pcapHopenOfflinePtr = mustLoad("pcap_hopen_offline")
}
func (h *pcapPkthdr) getSec() int64 {
return int64(h.Ts.Sec)
}
func (h *pcapPkthdr) getUsec() int64 {
return int64(h.Ts.Usec)
}
func (h *pcapPkthdr) getLen() int {
return int(h.Len)
}
func (h *pcapPkthdr) getCaplen() int {
return int(h.Caplen)
}
func statusError(status pcapCint) error {
var ret uintptr
if pcapStatustostrPtr == 0 {
ret, _, _ = syscall.Syscall(pcapStrerrorPtr, 1, uintptr(status), 0, 0)
} else {
ret, _, _ = syscall.Syscall(pcapStatustostrPtr, 1, uintptr(status), 0, 0)
}
return errors.New(bytePtrToString(ret))
}
func pcapGetTstampPrecision(cptr pcapTPtr) int {
if pcapGetTstampPrecisionPtr == 0 {
return pcapTstampPrecisionMicro
}
ret, _, _ := syscall.Syscall(pcapGetTstampPrecisionPtr, 1, uintptr(cptr), 0, 0)
return int(pcapCint(ret))
}
func pcapSetTstampPrecision(cptr pcapTPtr, precision int) error {
if pcapSetTstampPrecisionPtr == 0 {
return errors.New("Not supported")
}
ret, _, _ := syscall.Syscall(pcapSetTstampPrecisionPtr, 2, uintptr(cptr), uintptr(precision), 0)
if pcapCint(ret) < 0 {
return errors.New("Not supported")
}
return nil
}
func pcapOpenLive(device string, snaplen int, pro int, timeout int) (*Handle, error) {
buf := make([]byte, errorBufferSize)
dev, err := syscall.BytePtrFromString(device)
if err != nil {
return nil, err
}
cptr, _, _ := syscall.Syscall6(pcapOpenLivePtr, 5, uintptr(unsafe.Pointer(dev)), uintptr(snaplen), uintptr(pro), uintptr(timeout), uintptr(unsafe.Pointer(&buf[0])), 0)
if cptr == 0 {
return nil, errors.New(byteSliceToString(buf))
}
return &Handle{cptr: pcapTPtr(cptr)}, nil
}
func openOffline(file string) (handle *Handle, err error) {
buf := make([]byte, errorBufferSize)
f, err := syscall.BytePtrFromString(file)
if err != nil {
return nil, err
}
var cptr uintptr
if pcapOpenOfflineWithTstampPrecisionPtr == 0 {
cptr, _, _ = syscall.Syscall(pcapOpenOfflinePtr, 2, uintptr(unsafe.Pointer(f)), uintptr(unsafe.Pointer(&buf[0])), 0)
} else {
cptr, _, _ = syscall.Syscall(pcapOpenOfflineWithTstampPrecisionPtr, 3, uintptr(unsafe.Pointer(f)), uintptr(pcapTstampPrecisionNano), uintptr(unsafe.Pointer(&buf[0])))
}
if cptr == 0 {
return nil, errors.New(byteSliceToString(buf))
}
h := &Handle{cptr: pcapTPtr(cptr)}
return h, nil
}
func (p *Handle) pcapClose() {
if p.cptr != 0 {
_, _, _ = syscall.Syscall(pcapClosePtr, 1, uintptr(p.cptr), 0, 0)
}
p.cptr = 0
}
func (p *Handle) pcapGeterr() error {
ret, _, _ := syscall.Syscall(pcapGeterrPtr, 1, uintptr(p.cptr), 0, 0)
return errors.New(bytePtrToString(ret))
}
func (p *Handle) pcapStats() (*Stats, error) {
var cstats pcapStats
ret, _, _ := syscall.Syscall(pcapStatsPtr, 2, uintptr(p.cptr), uintptr(unsafe.Pointer(&cstats)), 0)
if pcapCint(ret) < 0 {
return nil, p.pcapGeterr()
}
return &Stats{
PacketsReceived: int(cstats.Recv),
PacketsDropped: int(cstats.Drop),
PacketsIfDropped: int(cstats.Ifdrop),
}, nil
}
// for libpcap < 1.8 pcap_compile is NOT thread-safe, so protect it.
var pcapCompileMu sync.Mutex
func (p *Handle) pcapCompile(expr string, maskp uint32) (pcapBpfProgram, error) {
var bpf pcapBpfProgram
cexpr, err := syscall.BytePtrFromString(expr)
if err != nil {
return pcapBpfProgram{}, err
}
pcapCompileMu.Lock()
defer pcapCompileMu.Unlock()
res, _, _ := syscall.Syscall6(pcapCompilePtr, 5, uintptr(p.cptr), uintptr(unsafe.Pointer(&bpf)), uintptr(unsafe.Pointer(cexpr)), uintptr(1), uintptr(maskp), 0)
if pcapCint(res) < 0 {
return bpf, p.pcapGeterr()
}
return bpf, nil
}
func (p pcapBpfProgram) free() {
_, _, _ = syscall.Syscall(pcapFreecodePtr, 1, uintptr(unsafe.Pointer(&p)), 0, 0)
}
func (p pcapBpfProgram) toBPFInstruction() []BPFInstruction {
bpfInsn := (*[bpfInstructionBufferSize]pcapBpfInstruction)(unsafe.Pointer(p.Insns))[0:p.Len:p.Len]
bpfInstruction := make([]BPFInstruction, len(bpfInsn), len(bpfInsn))
for i, v := range bpfInsn {
bpfInstruction[i].Code = v.Code
bpfInstruction[i].Jt = v.Jt
bpfInstruction[i].Jf = v.Jf
bpfInstruction[i].K = v.K
}
return bpfInstruction
}
func pcapBpfProgramFromInstructions(bpfInstructions []BPFInstruction) pcapBpfProgram {
var bpf pcapBpfProgram
bpf.Len = uint32(len(bpfInstructions))
cbpfInsns, _, _ := syscall.Syscall(callocPtr, 2, uintptr(len(bpfInstructions)), uintptr(unsafe.Sizeof(bpfInstructions[0])), 0)
gbpfInsns := (*[bpfInstructionBufferSize]pcapBpfInstruction)(unsafe.Pointer(cbpfInsns))
for i, v := range bpfInstructions {
gbpfInsns[i].Code = v.Code
gbpfInsns[i].Jt = v.Jt
gbpfInsns[i].Jf = v.Jf
gbpfInsns[i].K = v.K
}
bpf.Insns = (*pcapBpfInstruction)(unsafe.Pointer(cbpfInsns))
return bpf
}
func pcapLookupnet(device string) (netp, maskp uint32, err error) {
buf := make([]byte, errorBufferSize)
dev, err := syscall.BytePtrFromString(device)
if err != nil {
return 0, 0, err
}
e, _, _ := syscall.Syscall6(pcapLookupnetPtr, 4, uintptr(unsafe.Pointer(dev)), uintptr(unsafe.Pointer(&netp)), uintptr(unsafe.Pointer(&maskp)), uintptr(unsafe.Pointer(&buf[0])), 0, 0)
if pcapCint(e) < 0 {
return 0, 0, errors.New(byteSliceToString(buf))
}
return
}
func (b *BPF) pcapOfflineFilter(ci gopacket.CaptureInfo, data []byte) bool {
var hdr pcapPkthdr
hdr.Ts.Sec = int32(ci.Timestamp.Unix())
hdr.Ts.Usec = int32(ci.Timestamp.Nanosecond() / 1000)
hdr.Caplen = uint32(len(data)) // Trust actual length over ci.Length.
hdr.Len = uint32(ci.Length)
e, _, _ := syscall.Syscall(pcapOfflineFilterPtr, 3, uintptr(unsafe.Pointer(&b.bpf)), uintptr(unsafe.Pointer(&hdr)), uintptr(unsafe.Pointer(&data[0])))
return e != 0
}
func (p *Handle) pcapSetfilter(bpf pcapBpfProgram) error {
e, _, _ := syscall.Syscall(pcapSetfilterPtr, 2, uintptr(p.cptr), uintptr(unsafe.Pointer(&bpf)), 0)
if pcapCint(e) < 0 {
return p.pcapGeterr()
}
return nil
}
func (p *Handle) pcapListDatalinks() (datalinks []Datalink, err error) {
var dltbuf *pcapCint
ret, _, _ := syscall.Syscall(pcapListDatalinksPtr, 2, uintptr(p.cptr), uintptr(unsafe.Pointer(&dltbuf)), 0)
n := int(pcapCint(ret))
if n < 0 {
return nil, p.pcapGeterr()
}
defer syscall.Syscall(pcapFreeDatalinksPtr, 1, uintptr(unsafe.Pointer(dltbuf)), 0, 0)
datalinks = make([]Datalink, n)
dltArray := (*[1 << 28]pcapCint)(unsafe.Pointer(dltbuf))
for i := 0; i < n; i++ {
datalinks[i].Name = pcapDatalinkValToName(int((*dltArray)[i]))
datalinks[i].Description = pcapDatalinkValToDescription(int((*dltArray)[i]))
}
return datalinks, nil
}
func pcapOpenDead(linkType layers.LinkType, captureLength int) (*Handle, error) {
cptr, _, _ := syscall.Syscall(pcapOpenDeadPtr, 2, uintptr(linkType), uintptr(captureLength), 0)
if cptr == 0 {
return nil, errors.New("error opening dead capture")
}
return &Handle{cptr: pcapTPtr(cptr)}, nil
}
func (p *Handle) pcapNextPacketEx() NextError {
r, _, _ := syscall.Syscall(pcapNextExPtr, 3, uintptr(p.cptr), uintptr(unsafe.Pointer(&p.pkthdr)), uintptr(unsafe.Pointer(&p.bufptr)))
ret := pcapCint(r)
// According to https://github.com/the-tcpdump-group/libpcap/blob/1131a7c26c6f4d4772e4a2beeaf7212f4dea74ac/pcap.c#L398-L406 ,
// the return value of pcap_next_ex could be greater than 1 for success.
// Let's just make it 1 if it comes bigger than 1.
if ret > 1 {
ret = 1
}
return NextError(ret)
}
func (p *Handle) pcapDatalink() layers.LinkType {
ret, _, _ := syscall.Syscall(pcapDatalinkPtr, 1, uintptr(p.cptr), 0, 0)
return layers.LinkType(ret)
}
func (p *Handle) pcapSetDatalink(dlt layers.LinkType) error {
ret, _, _ := syscall.Syscall(pcapSetDatalinkPtr, 2, uintptr(p.cptr), uintptr(dlt), 0)
if pcapCint(ret) < 0 {
return p.pcapGeterr()
}
return nil
}
func pcapDatalinkValToName(dlt int) string {
ret, _, _ := syscall.Syscall(pcapDatalinkValToNamePtr, 1, uintptr(dlt), 0, 0)
return bytePtrToString(ret)
}
func pcapDatalinkValToDescription(dlt int) string {
ret, _, _ := syscall.Syscall(pcapDatalinkValToDescriptionPtr, 1, uintptr(dlt), 0, 0)
return bytePtrToString(ret)
}
func pcapDatalinkNameToVal(name string) int {
cptr, err := syscall.BytePtrFromString(name)
if err != nil {
return 0
}
ret, _, _ := syscall.Syscall(pcapDatalinkNameToValPtr, 1, uintptr(unsafe.Pointer(cptr)), 0, 0)
return int(pcapCint(ret))
}
func pcapLibVersion() string {
ret, _, _ := syscall.Syscall(pcapLibVersionPtr, 0, 0, 0, 0)
return bytePtrToString(ret)
}
func (p *Handle) isOpen() bool {
return p.cptr != 0
}
type pcapDevices struct {
all, cur *pcapIf
}
func (p pcapDevices) free() {
syscall.Syscall(pcapFreealldevsPtr, 1, uintptr(unsafe.Pointer(p.all)), 0, 0)
}
func (p *pcapDevices) next() bool {
if p.cur == nil {
p.cur = p.all
if p.cur == nil {
return false
}
return true
}
if p.cur.Next == nil {
return false
}
p.cur = p.cur.Next
return true
}
func (p pcapDevices) name() string {
return bytePtrToString(uintptr(unsafe.Pointer(p.cur.Name)))
}
func (p pcapDevices) description() string {
return bytePtrToString(uintptr(unsafe.Pointer(p.cur.Description)))
}
func (p pcapDevices) flags() uint32 {
return p.cur.Flags
}
type pcapAddresses struct {
all, cur *pcapAddr
}
func (p *pcapAddresses) next() bool {
if p.cur == nil {
p.cur = p.all
if p.cur == nil {
return false
}
return true
}
if p.cur.Next == nil {
return false
}
p.cur = p.cur.Next
return true
}
func (p pcapAddresses) addr() *syscall.RawSockaddr {
return p.cur.Addr
}
func (p pcapAddresses) netmask() *syscall.RawSockaddr {
return p.cur.Netmask
}
func (p pcapAddresses) broadaddr() *syscall.RawSockaddr {
return p.cur.Broadaddr
}
func (p pcapAddresses) dstaddr() *syscall.RawSockaddr {
return p.cur.Dstaddr
}
func (p pcapDevices) addresses() pcapAddresses {
return pcapAddresses{all: p.cur.Addresses}
}
func pcapFindAllDevs() (pcapDevices, error) {
buf := make([]byte, errorBufferSize)
var alldevsp pcapDevices
ret, _, _ := syscall.Syscall(pcapFindalldevsPtr, 2, uintptr(unsafe.Pointer(&alldevsp.all)), uintptr(unsafe.Pointer(&buf[0])), 0)
if pcapCint(ret) < 0 {
return pcapDevices{}, errors.New(byteSliceToString(buf))
}
return alldevsp, nil
}
func (p *Handle) pcapSendpacket(data []byte) error {
ret, _, _ := syscall.Syscall(pcapSendpacketPtr, 3, uintptr(p.cptr), uintptr(unsafe.Pointer(&data[0])), uintptr(len(data)))
if pcapCint(ret) < 0 {
return p.pcapGeterr()
}
return nil
}
func (p *Handle) pcapSetdirection(direction Direction) error {
status, _, _ := syscall.Syscall(pcapSetdirectionPtr, 2, uintptr(p.cptr), uintptr(direction), 0)
if pcapCint(status) < 0 {
return statusError(pcapCint(status))
}
return nil
}
func (p *Handle) pcapSnapshot() int {
ret, _, _ := syscall.Syscall(pcapSnapshotPtr, 1, uintptr(p.cptr), 0, 0)
return int(pcapCint(ret))
}
func (t TimestampSource) pcapTstampTypeValToName() string {
//libpcap <1.2 doesn't have pcap_*_tstamp_* functions
if pcapTstampTypeValToNamePtr == 0 {
return "pcap timestamp types not supported"
}
ret, _, _ := syscall.Syscall(pcapTstampTypeValToNamePtr, 1, uintptr(t), 0, 0)
return bytePtrToString(ret)
}
func pcapTstampTypeNameToVal(s string) (TimestampSource, error) {
//libpcap <1.2 doesn't have pcap_*_tstamp_* functions
if pcapTstampTypeNameToValPtr == 0 {
return 0, statusError(pcapCint(pcapError))
}
cs, err := syscall.BytePtrFromString(s)
if err != nil {
return 0, err
}
ret, _, _ := syscall.Syscall(pcapTstampTypeNameToValPtr, 1, uintptr(unsafe.Pointer(cs)), 0, 0)
t := pcapCint(ret)
if t < 0 {
return 0, statusError(pcapCint(t))
}
return TimestampSource(t), nil
}
func (p *InactiveHandle) pcapGeterr() error {
ret, _, _ := syscall.Syscall(pcapGeterrPtr, 1, uintptr(p.cptr), 0, 0)
return errors.New(bytePtrToString(ret))
}
func (p *InactiveHandle) pcapActivate() (*Handle, activateError) {
r, _, _ := syscall.Syscall(pcapActivatePtr, 1, uintptr(p.cptr), 0, 0)
ret := activateError(pcapCint(r))
if ret != aeNoError {
return nil, ret
}
h := &Handle{
cptr: p.cptr,
}
p.cptr = 0
return h, ret
}
func (p *InactiveHandle) pcapClose() {
if p.cptr != 0 {
_, _, _ = syscall.Syscall(pcapClosePtr, 1, uintptr(p.cptr), 0, 0)
}
p.cptr = 0
}
func pcapCreate(device string) (*InactiveHandle, error) {
buf := make([]byte, errorBufferSize)
dev, err := syscall.BytePtrFromString(device)
if err != nil {
return nil, err
}
cptr, _, _ := syscall.Syscall(pcapCreatePtr, 2, uintptr(unsafe.Pointer(dev)), uintptr(unsafe.Pointer(&buf[0])), 0)
if cptr == 0 {
return nil, errors.New(byteSliceToString(buf))
}
return &InactiveHandle{cptr: pcapTPtr(cptr)}, nil
}
func (p *InactiveHandle) pcapSetSnaplen(snaplen int) error {
status, _, _ := syscall.Syscall(pcapSetSnaplenPtr, 2, uintptr(p.cptr), uintptr(snaplen), 0)
if pcapCint(status) < 0 {
return statusError(pcapCint(status))
}
return nil
}
func (p *InactiveHandle) pcapSetPromisc(promisc bool) error {
var pro uintptr
if promisc {
pro = 1
}
status, _, _ := syscall.Syscall(pcapSetPromiscPtr, 2, uintptr(p.cptr), pro, 0)
if pcapCint(status) < 0 {
return statusError(pcapCint(status))
}
return nil
}
func (p *InactiveHandle) pcapSetTimeout(timeout time.Duration) error {
status, _, _ := syscall.Syscall(pcapSetTimeoutPtr, 2, uintptr(p.cptr), uintptr(timeoutMillis(timeout)), 0)
if pcapCint(status) < 0 {
return statusError(pcapCint(status))
}
return nil
}
func (p *InactiveHandle) pcapListTstampTypes() (out []TimestampSource) {
//libpcap <1.2 doesn't have pcap_*_tstamp_* functions
if pcapListTstampTypesPtr == 0 {
return
}
var types *pcapCint
ret, _, _ := syscall.Syscall(pcapListTstampTypesPtr, 2, uintptr(p.cptr), uintptr(unsafe.Pointer(&types)), 0)
n := int(pcapCint(ret))
if n < 0 {
return // public interface doesn't have error :(
}
defer syscall.Syscall(pcapFreeTstampTypesPtr, 1, uintptr(unsafe.Pointer(types)), 0, 0)
typesArray := (*[1 << 28]pcapCint)(unsafe.Pointer(types))
for i := 0; i < n; i++ {
out = append(out, TimestampSource((*typesArray)[i]))
}
return
}
func (p *InactiveHandle) pcapSetTstampType(t TimestampSource) error {
//libpcap <1.2 doesn't have pcap_*_tstamp_* functions
if pcapSetTstampTypePtr == 0 {
return statusError(pcapError)
}
status, _, _ := syscall.Syscall(pcapSetTstampTypePtr, 2, uintptr(p.cptr), uintptr(t), 0)
if pcapCint(status) < 0 {
return statusError(pcapCint(status))
}
return nil
}
func (p *InactiveHandle) pcapSetRfmon(monitor bool) error {
//winpcap does not support rfmon
if pcapCanSetRfmonPtr == 0 {
return CannotSetRFMon
}
var mon uintptr
if monitor {
mon = 1
}
canset, _, _ := syscall.Syscall(pcapCanSetRfmonPtr, 1, uintptr(p.cptr), 0, 0)
switch canset {
case 0:
return CannotSetRFMon
case 1:
// success
default:
return statusError(pcapCint(canset))
}
status, _, _ := syscall.Syscall(pcapSetRfmonPtr, 2, uintptr(p.cptr), mon, 0)
if status != 0 {
return statusError(pcapCint(status))
}
return nil
}
func (p *InactiveHandle) pcapSetBufferSize(bufferSize int) error {
status, _, _ := syscall.Syscall(pcapSetBufferSizePtr, 2, uintptr(p.cptr), uintptr(bufferSize), 0)
if pcapCint(status) < 0 {
return statusError(pcapCint(status))
}
return nil
}
func (p *InactiveHandle) pcapSetImmediateMode(mode bool) error {
//libpcap <1.5 does not have pcap_set_immediate_mode
if pcapSetImmediateModePtr == 0 {
return statusError(pcapError)
}
var md uintptr
if mode {
md = 1
}
status, _, _ := syscall.Syscall(pcapSetImmediateModePtr, 2, uintptr(p.cptr), md, 0)
if pcapCint(status) < 0 {
return statusError(pcapCint(status))
}
return nil
}
func (p *Handle) setNonBlocking() error {
// do nothing
return nil
@ -32,13 +793,18 @@ func (p *Handle) waitForPacket() {
// openOfflineFile returns contents of input file as a *Handle.
func openOfflineFile(file *os.File) (handle *Handle, err error) {
buf := (*C.char)(C.calloc(errorBufferSize, 1))
defer C.free(unsafe.Pointer(buf))
cf := C.intptr_t(file.Fd())
buf := make([]byte, errorBufferSize)
cf := file.Fd()
cptr := C.pcap_hopen_offline(cf, buf)
if cptr == nil {
return nil, errors.New(C.GoString(buf))
var cptr uintptr
if pcapOpenOfflineWithTstampPrecisionPtr == 0 {
cptr, _, _ = syscall.Syscall(pcapHopenOfflinePtr, 2, cf, uintptr(unsafe.Pointer(&buf[0])), 0)
} else {
cptr, _, _ = syscall.Syscall(pcapHOpenOfflineWithTstampPrecisionPtr, 3, cf, uintptr(pcapTstampPrecisionNano), uintptr(unsafe.Pointer(&buf[0])))
}
return &Handle{cptr: cptr}, nil
if cptr == 0 {
return nil, errors.New(byteSliceToString(buf))
}
return &Handle{cptr: pcapTPtr(cptr)}, nil
}

63
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
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
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
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
}

@ -15,6 +15,7 @@ import (
"bufio"
"compress/gzip"
"github.com/google/gopacket"
"github.com/google/gopacket/layers"
)
@ -40,6 +41,8 @@ type Reader struct {
linkType layers.LinkType
// reusable buffer
buf [16]byte
// buffer for ZeroCopyReadPacketData
packetBuf []byte
}
const magicNanoseconds = 0xA1B23C4D
@ -121,7 +124,11 @@ func (r *Reader) ReadPacketData() (data []byte, ci gopacket.CaptureInfo, err err
return
}
if ci.CaptureLength > int(r.snaplen) {
err = fmt.Errorf("capture length exceeds snap length: %d > %d", 16+ci.CaptureLength, 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)
@ -129,6 +136,36 @@ func (r *Reader) ReadPacketData() (data []byte, ci gopacket.CaptureInfo, err err
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
@ -184,3 +221,11 @@ func (r *Reader) SetSnaplen(newSnaplen uint32) {
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
}

@ -4,8 +4,6 @@
// 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.
package pcapgo
import (

72
vendor/github.com/google/gopacket/time.go generated vendored Normal file

@ -0,0 +1,72 @@
// 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 gopacket
import (
"fmt"
"math"
"time"
)
// TimestampResolution represents the resolution of timestamps in Base^Exponent.
type TimestampResolution struct {
Base, Exponent int
}
func (t TimestampResolution) String() string {
return fmt.Sprintf("%d^%d", t.Base, t.Exponent)
}
// ToDuration returns the smallest representable time difference as a time.Duration
func (t TimestampResolution) ToDuration() time.Duration {
if t.Base == 0 {
return 0
}
if t.Exponent == 0 {
return time.Second
}
switch t.Base {
case 10:
return time.Duration(math.Pow10(t.Exponent + 9))
case 2:
if t.Exponent < 0 {
return time.Second >> uint(-t.Exponent)
}
return time.Second << uint(t.Exponent)
default:
// this might loose precision
return time.Duration(float64(time.Second) * math.Pow(float64(t.Base), float64(t.Exponent)))
}
}
// TimestampResolutionInvalid represents an invalid timestamp resolution
var TimestampResolutionInvalid = TimestampResolution{}
// TimestampResolutionMillisecond is a resolution of 10^-3s
var TimestampResolutionMillisecond = TimestampResolution{10, -3}
// TimestampResolutionMicrosecond is a resolution of 10^-6s
var TimestampResolutionMicrosecond = TimestampResolution{10, -6}
// TimestampResolutionNanosecond is a resolution of 10^-9s
var TimestampResolutionNanosecond = TimestampResolution{10, -9}
// TimestampResolutionNTP is the resolution of NTP timestamps which is 2^-32 ≈ 233 picoseconds
var TimestampResolutionNTP = TimestampResolution{2, -32}
// TimestampResolutionCaptureInfo is the resolution used in CaptureInfo, which his currently nanosecond
var TimestampResolutionCaptureInfo = TimestampResolutionNanosecond
// PacketSourceResolution is an interface for packet data sources that
// support reporting the timestamp resolution of the aqcuired timestamps.
// Returned timestamps will always have NanosecondTimestampResolution due
// to the use of time.Time, but scaling might have occured if acquired
// timestamps have a different resolution.
type PacketSourceResolution interface {
// Resolution returns the timestamp resolution of acquired timestamps before scaling to NanosecondTimestampResolution.
Resolution() TimestampResolution
}