mirror of https://github.com/subgraph/fw-daemon
parent
4babfbbd14
commit
70c21b0636
@ -0,0 +1,38 @@
|
||||
# Compiled Object files, Static and Dynamic libs (Shared Objects)
|
||||
*.o
|
||||
*.a
|
||||
*.so
|
||||
|
||||
# Folders
|
||||
_obj
|
||||
_test
|
||||
|
||||
# Architecture specific extensions/prefixes
|
||||
*.[568vq]
|
||||
[568vq].out
|
||||
|
||||
*.cgo1.go
|
||||
*.cgo2.c
|
||||
_cgo_defun.c
|
||||
_cgo_gotypes.go
|
||||
_cgo_export.*
|
||||
|
||||
_testmain.go
|
||||
|
||||
*.exe
|
||||
#*
|
||||
*~
|
||||
|
||||
# examples binaries
|
||||
examples/synscan/synscan
|
||||
examples/pfdump/pfdump
|
||||
examples/pcapdump/pcapdump
|
||||
examples/httpassembly/httpassembly
|
||||
examples/statsassembly/statsassembly
|
||||
examples/arpscan/arpscan
|
||||
examples/bidirectional/bidirectional
|
||||
examples/bytediff/bytediff
|
||||
examples/reassemblydump/reassemblydump
|
||||
layers/gen
|
||||
macs/gen
|
||||
pcap/pcap_tester
|
@ -0,0 +1,7 @@
|
||||
#!/bin/bash
|
||||
|
||||
cd "$(dirname $0)"
|
||||
if [ -n "$(go fmt ./...)" ]; then
|
||||
echo "Go code is not formatted, run 'go fmt github.com/google/stenographer/...'" >&2
|
||||
exit 1
|
||||
fi
|
@ -0,0 +1,25 @@
|
||||
#!/bin/bash
|
||||
|
||||
cd "$(dirname $0)"
|
||||
|
||||
go get github.com/golang/lint/golint
|
||||
DIRS=". tcpassembly tcpassembly/tcpreader ip4defrag reassembly macs pcapgo pcap afpacket pfring routing"
|
||||
# Add subdirectories here as we clean up golint on each.
|
||||
for subdir in $DIRS; do
|
||||
pushd $subdir
|
||||
if golint |
|
||||
grep -v CannotSetRFMon | # pcap exported error name
|
||||
grep -v DataLost | # tcpassembly/tcpreader exported error name
|
||||
grep .; then
|
||||
exit 1
|
||||
fi
|
||||
popd
|
||||
done
|
||||
|
||||
pushd layers
|
||||
for file in $(cat .linted); do
|
||||
if golint $file | grep .; then
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
popd
|
@ -0,0 +1,10 @@
|
||||
#!/bin/bash
|
||||
|
||||
cd "$(dirname $0)"
|
||||
DIRS=". layers pcap pcapgo pfring tcpassembly tcpassembly/tcpreader routing ip4defrag bytediff macs"
|
||||
set -e
|
||||
for subdir in $DIRS; do
|
||||
pushd $subdir
|
||||
go vet
|
||||
popd
|
||||
done
|
@ -0,0 +1,14 @@
|
||||
language: go
|
||||
install:
|
||||
- go get github.com/google/gopacket
|
||||
- go get github.com/google/gopacket/layers
|
||||
- go get github.com/google/gopacket/tcpassembly
|
||||
- go get github.com/google/gopacket/reassembly
|
||||
script:
|
||||
- go test github.com/google/gopacket
|
||||
- go test github.com/google/gopacket/layers
|
||||
- go test github.com/google/gopacket/tcpassembly
|
||||
- go test github.com/google/gopacket/reassembly
|
||||
- ./.travis.gofmt.sh
|
||||
- ./.travis.govet.sh
|
||||
- ./.travis.golint.sh
|
@ -0,0 +1,46 @@
|
||||
AUTHORS AND MAINTAINERS:
|
||||
|
||||
MAIN DEVELOPERS:
|
||||
Graeme Connell <gconnell@google.com, gsconnell@gmail.com>
|
||||
|
||||
AUTHORS:
|
||||
Nigel Tao <nigeltao@google.com>
|
||||
Cole Mickens <cole.mickens@gmail.com>
|
||||
Ben Daglish <bdaglish@restorepoint.com>
|
||||
Luis Martinez <martinezlc99@gmail.com>
|
||||
Remco Verhoef <remco@dutchcoders.io>
|
||||
Hiroaki Kawai <Hiroaki.Kawai@gmail.com>
|
||||
Lukas Lueg <lukas.lueg@gmail.com>
|
||||
Laurent Hausermann <laurent.hausermann@gmail.com>
|
||||
Bill Green <bgreen@newrelic.com>
|
||||
|
||||
CONTRIBUTORS:
|
||||
Attila Olรกh <attila@attilaolah.eu>
|
||||
Vittus Mikiassen <matt.miki.vimik@gmail.com>
|
||||
Matthias Radestock <matthias.radestock@gmail.com>
|
||||
Matthew Sackman <matthew@wellquite.org>
|
||||
Loic Prylli <loicp@google.com>
|
||||
Alexandre Fiori <fiorix@gmail.com>
|
||||
Adrian Tam <adrian.c.m.tam@gmail.com>
|
||||
Satoshi Matsumoto <kaorimatz@gmail.com>
|
||||
David Stainton <dstainton415@gmail.com>
|
||||
Jesse Ward <jesse@jesseward.com>
|
||||
Kane Mathers <kane@kanemathers.name>
|
||||
|
||||
-----------------------------------------------
|
||||
FORKED FROM github.com/akrennmair/gopcap
|
||||
ALL THE FOLLOWING ARE FOR THAT PROJECT
|
||||
|
||||
MAIN DEVELOPERS:
|
||||
Andreas Krennmair <ak@synflood.at>
|
||||
|
||||
CONTRIBUTORS:
|
||||
Andrea Nall <anall@andreanall.com>
|
||||
Daniel Arndt <danielarndt@gmail.com>
|
||||
Dustin Sallings <dustin@spy.net>
|
||||
Graeme Connell <gconnell@google.com, gsconnell@gmail.com>
|
||||
Guillaume Savary <guillaume@savary.name>
|
||||
Mark Smith <mark@qq.is>
|
||||
Miek Gieben <miek@miek.nl>
|
||||
Mike Bell <mike@mikebell.org>
|
||||
Trevor Strohman <strohman@google.com>
|
@ -0,0 +1,215 @@
|
||||
Contributing To gopacket
|
||||
========================
|
||||
|
||||
So you've got some code and you'd like it to be part of gopacket... wonderful!
|
||||
We're happy to accept contributions, whether they're fixes to old protocols, new
|
||||
protocols entirely, or anything else you think would improve the gopacket
|
||||
library. This document is designed to help you to do just that.
|
||||
|
||||
The first section deals with the plumbing: how to actually get a change
|
||||
submitted.
|
||||
|
||||
The second section deals with coding style... Go is great in that it
|
||||
has a uniform style implemented by 'go fmt', but there's still some decisions
|
||||
we've made that go above and beyond, and if you follow them, they won't come up
|
||||
in your code review.
|
||||
|
||||
The third section deals with some of the implementation decisions we've made,
|
||||
which may help you to understand the current code and which we may ask you to
|
||||
conform to (or provide compelling reasons for ignoring).
|
||||
|
||||
Overall, we hope this document will help you to understand our system and write
|
||||
great code which fits in, and help us to turn around on your code review quickly
|
||||
so the code can make it into the master branch as quickly as possible.
|
||||
|
||||
|
||||
How To Submit Code
|
||||
------------------
|
||||
|
||||
We use github.com's Pull Request feature to receive code contributions from
|
||||
external contributors. See
|
||||
https://help.github.com/articles/creating-a-pull-request/ for details on
|
||||
how to create a request.
|
||||
|
||||
Also, there's a local script `gc` in the base directory of GoPacket that
|
||||
runs a local set of checks, which should give you relatively high confidence
|
||||
that your pull won't fail github pull checks.
|
||||
|
||||
```sh
|
||||
go get github.com/google/gopacket
|
||||
cd $GOROOT/src/pkg/github.com/google/gopacket
|
||||
git checkout -b <mynewfeature> # create a new branch to work from
|
||||
... code code code ...
|
||||
./gc # Run this to do local commits, it performs a number of checks
|
||||
```
|
||||
|
||||
To sum up:
|
||||
|
||||
* DO
|
||||
+ Pull down the latest version.
|
||||
+ Make a feature-specific branch.
|
||||
+ Code using the style and methods discussed in the rest of this document.
|
||||
+ Use the ./gc command to do local commits or check correctness.
|
||||
+ Push your new feature branch up to github.com, as a pull request.
|
||||
+ Handle comments and requests from reviewers, pushing new commits up to
|
||||
your feature branch as problems are addressed.
|
||||
+ Put interesting comments and discussions into commit comments.
|
||||
* DON'T
|
||||
+ Push to someone else's branch without their permission.
|
||||
|
||||
|
||||
Coding Style
|
||||
------------
|
||||
|
||||
* Go code must be run through `go fmt`, `go vet`, and `golint`
|
||||
* Follow http://golang.org/doc/effective_go.html as much as possible.
|
||||
+ In particular, http://golang.org/doc/effective_go.html#mixed-caps. Enums
|
||||
should be be CamelCase, with acronyms capitalized (TCPSourcePort, vs.
|
||||
TcpSourcePort or TCP_SOURCE_PORT).
|
||||
* Bonus points for giving enum types a String() field.
|
||||
* Any exported types or functions should have commentary
|
||||
(http://golang.org/doc/effective_go.html#commentary)
|
||||
|
||||
|
||||
Coding Methods And Implementation Notes
|
||||
---------------------------------------
|
||||
|
||||
### Error Handling
|
||||
|
||||
Many times, you'll be decoding a protocol and run across something bad, a packet
|
||||
corruption or the like. How do you handle this? First off, ALWAYS report the
|
||||
error. You can do this either by returning the error from the decode() function
|
||||
(most common), or if you're up for it you can implement and add an ErrorLayer
|
||||
through the packet builder (the first method is a simple shortcut that does
|
||||
exactly this, then stops any future decoding).
|
||||
|
||||
Often, you'll already have decode some part of your protocol by the time you hit
|
||||
your error. Use your own discretion to determine whether the stuff you've
|
||||
already decoded should be returned to the caller or not:
|
||||
|
||||
```go
|
||||
func decodeMyProtocol(data []byte, p gopacket.PacketBuilder) error {
|
||||
prot := &MyProtocol{}
|
||||
if len(data) < 10 {
|
||||
// This error occurred before we did ANYTHING, so there's nothing in my
|
||||
// protocol that the caller could possibly want. Just return the error.
|
||||
return fmt.Errorf("Length %d less than 10", len(data))
|
||||
}
|
||||
prot.ImportantField1 = data[:5]
|
||||
prot.ImportantField2 = data[5:10]
|
||||
// At this point, we've already got enough information in 'prot' to
|
||||
// warrant returning it to the caller, so we'll add it now.
|
||||
p.AddLayer(prot)
|
||||
if len(data) < 15 {
|
||||
// We encountered an error later in the packet, but the caller already
|
||||
// has the important info we've gleaned so far.
|
||||
return fmt.Errorf("Length %d less than 15", len(data))
|
||||
}
|
||||
prot.ImportantField3 = data[10:15]
|
||||
return nil // We've already added the layer, we can just return success.
|
||||
}
|
||||
```
|
||||
|
||||
In general, our code follows the approach of returning the first error it
|
||||
encounters. In general, we don't trust any bytes after the first error we see.
|
||||
|
||||
### What Is A Layer?
|
||||
|
||||
The definition of a layer is up to the discretion of the coder. It should be
|
||||
something important enough that it's actually useful to the caller (IE: every
|
||||
TLV value should probably NOT be a layer). However, it can be more granular
|
||||
than a single protocol... IPv6 and SCTP both implement many layers to handle the
|
||||
various parts of the protocol. Use your best judgement, and prepare to defend
|
||||
your decisions during code review. ;)
|
||||
|
||||
### Performance
|
||||
|
||||
We strive to make gopacket as fast as possible while still providing lots of
|
||||
features. In general, this means:
|
||||
|
||||
* Focus performance tuning on common protocols (IP4/6, TCP, etc), and optimize
|
||||
others on an as-needed basis (tons of MPLS on your network? Time to optimize
|
||||
MPLS!)
|
||||
* Use fast operations. See the toplevel benchmark_test for benchmarks of some
|
||||
of Go's underlying features and types.
|
||||
* Test your performance changes! You should use the ./gc script's --benchmark
|
||||
flag to submit any performance-related changes. Use pcap/gopacket_benchmark
|
||||
to test your change against a PCAP file based on your traffic patterns.
|
||||
* Don't be TOO hacky. Sometimes, removing an unused struct from a field causes
|
||||
a huge performance hit, due to the way that Go currently handles its segmented
|
||||
stack... don't be afraid to clean it up anyway. We'll trust the Go compiler
|
||||
to get good enough over time to handle this. Also, this type of
|
||||
compiler-specific optimization is very fragile; someone adding a field to an
|
||||
entirely different struct elsewhere in the codebase could reverse any gains
|
||||
you might achieve by aligning your allocations.
|
||||
* Try to minimize memory allocations. If possible, use []byte to reference
|
||||
pieces of the input, instead of using string, which requires copying the bytes
|
||||
into a new memory allocation.
|
||||
* Think hard about what should be evaluated lazily vs. not. In general, a
|
||||
layer's struct should almost exactly mirror the layer's frame. Anything
|
||||
that's more interesting should be a function. This may not always be
|
||||
possible, but it's a good rule of thumb.
|
||||
* Don't fear micro-optimizations. With the above in mind, we welcome
|
||||
micro-optimizations that we think will have positive/neutral impacts on the
|
||||
majority of workloads. A prime example of this is pre-allocating certain
|
||||
structs within a larger one:
|
||||
|
||||
```go
|
||||
type MyProtocol struct {
|
||||
// Most packets have 1-4 of VeryCommon, so we preallocate it here.
|
||||
initialAllocation [4]uint32
|
||||
VeryCommon []uint32
|
||||
}
|
||||
|
||||
func decodeMyProtocol(data []byte, p gopacket.PacketBuilder) error {
|
||||
prot := &MyProtocol{}
|
||||
prot.VeryCommon = proto.initialAllocation[:0]
|
||||
for len(data) > 4 {
|
||||
field := binary.BigEndian.Uint32(data[:4])
|
||||
data = data[4:]
|
||||
// Since we're using the underlying initialAllocation, we won't need to
|
||||
// allocate new memory for the following append unless we more than 16
|
||||
// bytes of data, which should be the uncommon case.
|
||||
prot.VeryCommon = append(prot.VeryCommon, field)
|
||||
}
|
||||
p.AddLayer(prot)
|
||||
if len(data) > 0 {
|
||||
return fmt.Errorf("MyProtocol packet has %d bytes left after decoding", len(data))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
```
|
||||
|
||||
### Slices And Data
|
||||
|
||||
If you're pulling a slice from the data you're decoding, don't copy it. Just
|
||||
use the slice itself.
|
||||
|
||||
```go
|
||||
type MyProtocol struct {
|
||||
A, B net.IP
|
||||
}
|
||||
func decodeMyProtocol(data []byte, p gopacket.PacketBuilder) error {
|
||||
p.AddLayer(&MyProtocol{
|
||||
A: data[:4],
|
||||
B: data[4:8],
|
||||
})
|
||||
return nil
|
||||
}
|
||||
```
|
||||
|
||||
The caller has already agreed, by using this library, that they won't modify the
|
||||
set of bytes they pass in to the decoder, or the library has already copied the
|
||||
set of bytes to a read-only location. See DecodeOptions.NoCopy for more
|
||||
information.
|
||||
|
||||
### Enums/Types
|
||||
|
||||
If a protocol has an integer field (uint8, uint16, etc) with a couple of known
|
||||
values that mean something special, make it a type. This allows us to do really
|
||||
nice things like adding a String() function to them, so we can more easily
|
||||
display those to users. Check out layers/enums.go for one example, as well as
|
||||
layers/icmp.go for layer-specific enums.
|
||||
|
||||
When naming things, try for descriptiveness over suscinctness. For example,
|
||||
choose DNSResponseRecord over DNSRR.
|
@ -0,0 +1,28 @@
|
||||
Copyright (c) 2012 Google, Inc. All rights reserved.
|
||||
Copyright (c) 2009-2011 Andreas Krennmair. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
* Neither the name of Andreas Krennmair, Google, nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
@ -0,0 +1,10 @@
|
||||
# GoPacket
|
||||
|
||||
This library provides packet decoding capabilities for Go.
|
||||
See [godoc](https://godoc.org/github.com/google/gopacket) for more details.
|
||||
|
||||
[](https://travis-ci.org/google/gopacket)
|
||||
[](https://godoc.org/github.com/google/gopacket)
|
||||
|
||||
Originally forked from the gopcap project written by Andreas
|
||||
Krennmair <ak@synflood.at> (http://github.com/akrennmair/gopcap).
|
@ -0,0 +1,178 @@
|
||||
// Copyright 2012 Google, Inc. All rights reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the LICENSE file in the root of the source
|
||||
// tree.
|
||||
|
||||
package gopacket
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// Layer represents a single decoded packet layer (using either the
|
||||
// OSI or TCP/IP definition of a layer). When decoding, a packet's data is
|
||||
// broken up into a number of layers. The caller may call LayerType() to
|
||||
// figure out which type of layer they've received from the packet. Optionally,
|
||||
// they may then use a type assertion to get the actual layer type for deep
|
||||
// inspection of the data.
|
||||
type Layer interface {
|
||||
// LayerType is the gopacket type for this layer.
|
||||
LayerType() LayerType
|
||||
// LayerContents returns the set of bytes that make up this layer.
|
||||
LayerContents() []byte
|
||||
// LayerPayload returns the set of bytes contained within this layer, not
|
||||
// including the layer itself.
|
||||
LayerPayload() []byte
|
||||
}
|
||||
|
||||
// Payload is a Layer containing the payload of a packet. The definition of
|
||||
// what constitutes the payload of a packet depends on previous layers; for
|
||||
// TCP and UDP, we stop decoding above layer 4 and return the remaining
|
||||
// bytes as a Payload. Payload is an ApplicationLayer.
|
||||
type Payload []byte
|
||||
|
||||
// LayerType returns LayerTypePayload
|
||||
func (p Payload) LayerType() LayerType { return LayerTypePayload }
|
||||
|
||||
// LayerContents returns the bytes making up this layer.
|
||||
func (p Payload) LayerContents() []byte { return []byte(p) }
|
||||
|
||||
// LayerPayload returns the payload within this layer.
|
||||
func (p Payload) LayerPayload() []byte { return nil }
|
||||
|
||||
// Payload returns this layer as bytes.
|
||||
func (p Payload) Payload() []byte { return []byte(p) }
|
||||
|
||||
// String implements fmt.Stringer.
|
||||
func (p Payload) String() string { return fmt.Sprintf("%d byte(s)", len(p)) }
|
||||
|
||||
// GoString implements fmt.GoStringer.
|
||||
func (p Payload) GoString() string { return LongBytesGoString([]byte(p)) }
|
||||
|
||||
// CanDecode implements DecodingLayer.
|
||||
func (p Payload) CanDecode() LayerClass { return LayerTypePayload }
|
||||
|
||||
// NextLayerType implements DecodingLayer.
|
||||
func (p Payload) NextLayerType() LayerType { return LayerTypeZero }
|
||||
|
||||
// DecodeFromBytes implements DecodingLayer.
|
||||
func (p *Payload) DecodeFromBytes(data []byte, df DecodeFeedback) error {
|
||||
*p = Payload(data)
|
||||
return nil
|
||||
}
|
||||
|
||||
// SerializeTo writes the serialized form of this layer into the
|
||||
// SerializationBuffer, implementing gopacket.SerializableLayer.
|
||||
// See the docs for gopacket.SerializableLayer for more info.
|
||||
func (p Payload) SerializeTo(b SerializeBuffer, opts SerializeOptions) error {
|
||||
bytes, err := b.PrependBytes(len(p))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
copy(bytes, p)
|
||||
return nil
|
||||
}
|
||||
|
||||
// decodePayload decodes data by returning it all in a Payload layer.
|
||||
func decodePayload(data []byte, p PacketBuilder) error {
|
||||
payload := &Payload{}
|
||||
if err := payload.DecodeFromBytes(data, p); err != nil {
|
||||
return nil
|
||||
}
|
||||
p.AddLayer(payload)
|
||||
p.SetApplicationLayer(payload)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Fragment is a Layer containing a fragment of a larger frame, used by layers
|
||||
// like IPv4 and IPv6 that allow for fragmentation of their payloads.
|
||||
type Fragment []byte
|
||||
|
||||
// LayerType returns LayerTypeFragment
|
||||
func (p *Fragment) LayerType() LayerType { return LayerTypeFragment }
|
||||
|
||||
// LayerContents implements Layer.
|
||||
func (p *Fragment) LayerContents() []byte { return []byte(*p) }
|
||||
|
||||
// LayerPayload implements Layer.
|
||||
func (p *Fragment) LayerPayload() []byte { return nil }
|
||||
|
||||
// Payload returns this layer as a byte slice.
|
||||
func (p *Fragment) Payload() []byte { return []byte(*p) }
|
||||
|
||||
// String implements fmt.Stringer.
|
||||
func (p *Fragment) String() string { return fmt.Sprintf("%d byte(s)", len(*p)) }
|
||||
|
||||
// CanDecode implements DecodingLayer.
|
||||
func (p *Fragment) CanDecode() LayerClass { return LayerTypeFragment }
|
||||
|
||||
// NextLayerType implements DecodingLayer.
|
||||
func (p *Fragment) NextLayerType() LayerType { return LayerTypeZero }
|
||||
|
||||
// DecodeFromBytes implements DecodingLayer.
|
||||
func (p *Fragment) DecodeFromBytes(data []byte, df DecodeFeedback) error {
|
||||
*p = Fragment(data)
|
||||
return nil
|
||||
}
|
||||
|
||||
// SerializeTo writes the serialized form of this layer into the
|
||||
// SerializationBuffer, implementing gopacket.SerializableLayer.
|
||||
// See the docs for gopacket.SerializableLayer for more info.
|
||||
func (p *Fragment) SerializeTo(b SerializeBuffer, opts SerializeOptions) error {
|
||||
bytes, err := b.PrependBytes(len(*p))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
copy(bytes, *p)
|
||||
return nil
|
||||
}
|
||||
|
||||
// decodeFragment decodes data by returning it all in a Fragment layer.
|
||||
func decodeFragment(data []byte, p PacketBuilder) error {
|
||||
payload := &Fragment{}
|
||||
if err := payload.DecodeFromBytes(data, p); err != nil {
|
||||
return nil
|
||||
}
|
||||
p.AddLayer(payload)
|
||||
p.SetApplicationLayer(payload)
|
||||
return nil
|
||||
}
|
||||
|
||||
// These layers correspond to Internet Protocol Suite (TCP/IP) layers, and their
|
||||
// corresponding OSI layers, as best as possible.
|
||||
|
||||
// LinkLayer is the packet layer corresponding to TCP/IP layer 1 (OSI layer 2)
|
||||
type LinkLayer interface {
|
||||
Layer
|
||||
LinkFlow() Flow
|
||||
}
|
||||
|
||||
// NetworkLayer is the packet layer corresponding to TCP/IP layer 2 (OSI
|
||||
// layer 3)
|
||||
type NetworkLayer interface {
|
||||
Layer
|
||||
NetworkFlow() Flow
|
||||
}
|
||||
|
||||
// TransportLayer is the packet layer corresponding to the TCP/IP layer 3 (OSI
|
||||
// layer 4)
|
||||
type TransportLayer interface {
|
||||
Layer
|
||||
TransportFlow() Flow
|
||||
}
|
||||
|
||||
// ApplicationLayer is the packet layer corresponding to the TCP/IP layer 4 (OSI
|
||||
// layer 7), also known as the packet payload.
|
||||
type ApplicationLayer interface {
|
||||
Layer
|
||||
Payload() []byte
|
||||
}
|
||||
|
||||
// ErrorLayer is a packet layer created when decoding of the packet has failed.
|
||||
// Its payload is all the bytes that we were unable to decode, and the returned
|
||||
// error details why the decoding failed.
|
||||
type ErrorLayer interface {
|
||||
Layer
|
||||
Error() error
|
||||
}
|
@ -0,0 +1,157 @@
|
||||
// Copyright 2012 Google, Inc. All rights reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the LICENSE file in the root of the source
|
||||
// tree.
|
||||
|
||||
package gopacket
|
||||
|
||||
import (
|
||||
"errors"
|
||||
)
|
||||
|
||||
// DecodeFeedback is used by DecodingLayer layers to provide decoding metadata.
|
||||
type DecodeFeedback interface {
|
||||
// SetTruncated should be called if during decoding you notice that a packet
|
||||
// is shorter than internal layer variables (HeaderLength, or the like) say it
|
||||
// should be. It sets packet.Metadata().Truncated.
|
||||
SetTruncated()
|
||||
}
|
||||
|
||||
type nilDecodeFeedback struct{}
|
||||
|
||||
func (nilDecodeFeedback) SetTruncated() {}
|
||||
|
||||
// NilDecodeFeedback implements DecodeFeedback by doing nothing.
|
||||
var NilDecodeFeedback DecodeFeedback = nilDecodeFeedback{}
|
||||
|
||||
// PacketBuilder is used by layer decoders to store the layers they've decoded,
|
||||
// and to defer future decoding via NextDecoder.
|
||||
// Typically, the pattern for use is:
|
||||
// func (m *myDecoder) Decode(data []byte, p PacketBuilder) error {
|
||||
// if myLayer, err := myDecodingLogic(data); err != nil {
|
||||
// return err
|
||||
// } else {
|
||||
// p.AddLayer(myLayer)
|
||||
// }
|
||||
// // maybe do this, if myLayer is a LinkLayer
|
||||
// p.SetLinkLayer(myLayer)
|
||||
// return p.NextDecoder(nextDecoder)
|
||||
// }
|
||||
type PacketBuilder interface {
|
||||
DecodeFeedback
|
||||
// AddLayer should be called by a decoder immediately upon successful
|
||||
// decoding of a layer.
|
||||
AddLayer(l Layer)
|
||||
// The following functions set the various specific layers in the final
|
||||
// packet. Note that if many layers call SetX, the first call is kept and all
|
||||
// other calls are ignored.
|
||||
SetLinkLayer(LinkLayer)
|
||||
SetNetworkLayer(NetworkLayer)
|
||||
SetTransportLayer(TransportLayer)
|
||||
SetApplicationLayer(ApplicationLayer)
|
||||
SetErrorLayer(ErrorLayer)
|
||||
// NextDecoder should be called by a decoder when they're done decoding a
|
||||
// packet layer but not done with decoding the entire packet. The next
|
||||
// decoder will be called to decode the last AddLayer's LayerPayload.
|
||||
// Because of this, NextDecoder must only be called once all other
|
||||
// PacketBuilder calls have been made. Set*Layer and AddLayer calls after
|
||||
// NextDecoder calls will behave incorrectly.
|
||||
NextDecoder(next Decoder) error
|
||||
// DumpPacketData is used solely for decoding. If you come across an error
|
||||
// you need to diagnose while processing a packet, call this and your packet's
|
||||
// data will be dumped to stderr so you can create a test. This should never
|
||||
// be called from a production decoder.
|
||||
DumpPacketData()
|
||||
// DecodeOptions returns the decode options
|
||||
DecodeOptions() *DecodeOptions
|
||||
}
|
||||
|
||||
// Decoder is an interface for logic to decode a packet layer. Users may
|
||||
// implement a Decoder to handle their own strange packet types, or may use one
|
||||
// of the many decoders available in the 'layers' subpackage to decode things
|
||||
// for them.
|
||||
type Decoder interface {
|
||||
// Decode decodes the bytes of a packet, sending decoded values and other
|
||||
// information to PacketBuilder, and returning an error if unsuccessful. See
|
||||
// the PacketBuilder documentation for more details.
|
||||
Decode([]byte, PacketBuilder) error
|
||||
}
|
||||
|
||||
// DecodeFunc wraps a function to make it a Decoder.
|
||||
type DecodeFunc func([]byte, PacketBuilder) error
|
||||
|
||||
// Decode implements Decoder by calling itself.
|
||||
func (d DecodeFunc) Decode(data []byte, p PacketBuilder) error {
|
||||
// function, call thyself.
|
||||
return d(data, p)
|
||||
}
|
||||
|
||||
// DecodePayload is a Decoder that returns a Payload layer containing all
|
||||
// remaining bytes.
|
||||
var DecodePayload Decoder = DecodeFunc(decodePayload)
|
||||
|
||||
// DecodeUnknown is a Decoder that returns an Unknown layer containing all
|
||||
// remaining bytes, useful if you run up against a layer that you're unable to
|
||||
// decode yet. This layer is considered an ErrorLayer.
|
||||
var DecodeUnknown Decoder = DecodeFunc(decodeUnknown)
|
||||
|
||||
// DecodeFragment is a Decoder that returns a Fragment layer containing all
|
||||
// remaining bytes.
|
||||
var DecodeFragment Decoder = DecodeFunc(decodeFragment)
|
||||
|
||||
// LayerTypeZero is an invalid layer type, but can be used to determine whether
|
||||
// layer type has actually been set correctly.
|
||||
var LayerTypeZero = RegisterLayerType(0, LayerTypeMetadata{Name: "Unknown", Decoder: DecodeUnknown})
|
||||
|
||||
// LayerTypeDecodeFailure is the layer type for the default error layer.
|
||||
var LayerTypeDecodeFailure = RegisterLayerType(1, LayerTypeMetadata{Name: "DecodeFailure", Decoder: DecodeUnknown})
|
||||
|
||||
// LayerTypePayload is the layer type for a payload that we don't try to decode
|
||||
// but treat as a success, IE: an application-level payload.
|
||||
var LayerTypePayload = RegisterLayerType(2, LayerTypeMetadata{Name: "Payload", Decoder: DecodePayload})
|
||||
|
||||
// LayerTypeFragment is the layer type for a fragment of a layer transported
|
||||
// by an underlying layer that supports fragmentation.
|
||||
var LayerTypeFragment = RegisterLayerType(3, LayerTypeMetadata{Name: "Fragment", Decoder: DecodeFragment})
|
||||
|
||||
// DecodeFailure is a packet layer created if decoding of the packet data failed
|
||||
// for some reason. It implements ErrorLayer. LayerContents will be the entire
|
||||
// set of bytes that failed to parse, and Error will return the reason parsing
|
||||
// failed.
|
||||
type DecodeFailure struct {
|
||||
data []byte
|
||||
err error
|
||||
stack []byte
|
||||
}
|
||||
|
||||
// Error returns the error encountered during decoding.
|
||||
func (d *DecodeFailure) Error() error { return d.err }
|
||||
|
||||
// LayerContents implements Layer.
|
||||
func (d *DecodeFailure) LayerContents() []byte { return d.data }
|
||||
|
||||
// LayerPayload implements Layer.
|
||||
func (d *DecodeFailure) LayerPayload() []byte { return nil }
|
||||
|
||||
// String implements fmt.Stringer.
|
||||
func (d *DecodeFailure) String() string {
|
||||
return "Packet decoding error: " + d.Error().Error()
|
||||
}
|
||||
|
||||
// Dump implements Dumper.
|
||||
func (d *DecodeFailure) Dump() (s string) {
|
||||
if d.stack != nil {
|
||||
s = string(d.stack)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// LayerType returns LayerTypeDecodeFailure
|
||||
func (d *DecodeFailure) LayerType() LayerType { return LayerTypeDecodeFailure }
|
||||
|
||||
// decodeUnknown "decodes" unsupported data types by returning an error.
|
||||
// This decoder will thus always return a DecodeFailure layer.
|
||||
func decodeUnknown(data []byte, p PacketBuilder) error {
|
||||
return errors.New("Layer type not currently supported")
|
||||
}
|
@ -0,0 +1,365 @@
|
||||
// Copyright 2012 Google, Inc. All rights reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the LICENSE file in the root of the source
|
||||
// tree.
|
||||
|
||||
/*
|
||||
Package gopacket provides packet decoding for the Go language.
|
||||
|
||||
gopacket contains many sub-packages with additional functionality you may find
|
||||
useful, including:
|
||||
|
||||
* layers: You'll probably use this every time. This contains of the logic
|
||||
built into gopacket for decoding packet protocols. Note that all example
|
||||
code below assumes that you have imported both gopacket and
|
||||
gopacket/layers.
|
||||
* pcap: C bindings to use libpcap to read packets off the wire.
|
||||
* pfring: C bindings to use PF_RING to read packets off the wire.
|
||||
* afpacket: C bindings for Linux's AF_PACKET to read packets off the wire.
|
||||
* tcpassembly: TCP stream reassembly
|
||||
|
||||
Also, if you're looking to dive right into code, see the examples subdirectory
|
||||
for numerous simple binaries built using gopacket libraries.
|
||||
|
||||
Basic Usage
|
||||
|
||||
gopacket takes in packet data as a []byte and decodes it into a packet with
|
||||
a non-zero number of "layers". Each layer corresponds to a protocol
|
||||
within the bytes. Once a packet has been decoded, the layers of the packet
|
||||
can be requested from the packet.
|
||||
|
||||
// Decode a packet
|
||||
packet := gopacket.NewPacket(myPacketData, layers.LayerTypeEthernet, gopacket.Default)
|
||||
// Get the TCP layer from this packet
|
||||
if tcpLayer := packet.Layer(layers.LayerTypeTCP); tcpLayer != nil {
|
||||
fmt.Println("This is a TCP packet!")
|
||||
// Get actual TCP data from this layer
|
||||
tcp, _ := tcpLayer.(*layers.TCP)
|
||||
fmt.Printf("From src port %d to dst port %d\n", tcp.SrcPort, tcp.DstPort)
|
||||
}
|
||||
// Iterate over all layers, printing out each layer type
|
||||
for _, layer := range packet.Layers() {
|
||||
fmt.Println("PACKET LAYER:", layer.LayerType())
|
||||
}
|
||||
|
||||
Packets can be decoded from a number of starting points. Many of our base
|
||||
types implement Decoder, which allow us to decode packets for which
|
||||
we don't have full data.
|
||||
|
||||
// Decode an ethernet packet
|
||||
ethP := gopacket.NewPacket(p1, layers.LayerTypeEthernet, gopacket.Default)
|
||||
// Decode an IPv6 header and everything it contains
|
||||
ipP := gopacket.NewPacket(p2, layers.LayerTypeIPv6, gopacket.Default)
|
||||
// Decode a TCP header and its payload
|
||||
tcpP := gopacket.NewPacket(p3, layers.LayerTypeTCP, gopacket.Default)
|
||||
|
||||
|
||||
Reading Packets From A Source
|
||||
|
||||
Most of the time, you won't just have a []byte of packet data lying around.
|
||||
Instead, you'll want to read packets in from somewhere (file, interface, etc)
|
||||
and process them. To do that, you'll want to build a PacketSource.
|
||||
|
||||
First, you'll need to construct an object that implements the PacketDataSource
|
||||
interface. There are implementations of this interface bundled with gopacket
|
||||
in the gopacket/pcap and gopacket/pfring subpackages... see their documentation
|
||||
for more information on their usage. Once you have a PacketDataSource, you can
|
||||
pass it into NewPacketSource, along with a Decoder of your choice, to create
|
||||
a PacketSource.
|
||||
|
||||
Once you have a PacketSource, you can read packets from it in multiple ways.
|
||||
See the docs for PacketSource for more details. The easiest method is the
|
||||
Packets function, which returns a channel, then asynchronously writes new
|
||||
packets into that channel, closing the channel if the packetSource hits an
|
||||
end-of-file.
|
||||
|
||||
packetSource := ... // construct using pcap or pfring
|
||||
for packet := range packetSource.Packets() {
|
||||
handlePacket(packet) // do something with each packet
|
||||
}
|
||||
|
||||
You can change the decoding options of the packetSource by setting fields in
|
||||
packetSource.DecodeOptions... see the following sections for more details.
|
||||
|
||||
|
||||
Lazy Decoding
|
||||
|
||||
gopacket optionally decodes packet data lazily, meaning it
|
||||
only decodes a packet layer when it needs to handle a function call.
|
||||
|
||||
// Create a packet, but don't actually decode anything yet
|
||||
packet := gopacket.NewPacket(myPacketData, layers.LayerTypeEthernet, gopacket.Lazy)
|
||||
// Now, decode the packet up to the first IPv4 layer found but no further.
|
||||
// If no IPv4 layer was found, the whole packet will be decoded looking for
|
||||
// it.
|
||||
ip4 := packet.Layer(layers.LayerTypeIPv4)
|
||||
// Decode all layers and return them. The layers up to the first IPv4 layer
|
||||
// are already decoded, and will not require decoding a second time.
|
||||
layers := packet.Layers()
|
||||
|
||||
Lazily-decoded packets are not concurrency-safe. Since layers have not all been
|
||||
decoded, each call to Layer() or Layers() has the potential to mutate the packet
|
||||
in order to decode the next layer. If a packet is used
|
||||
in multiple goroutines concurrently, don't use gopacket.Lazy. Then gopacket
|
||||
will decode the packet fully, and all future function calls won't mutate the
|
||||
object.
|
||||
|
||||
|
||||
NoCopy Decoding
|
||||
|
||||
By default, gopacket will copy the slice passed to NewPacket and store the
|
||||
copy within the packet, so future mutations to the bytes underlying the slice
|
||||
don't affect the packet and its layers. If you can guarantee that the
|
||||
underlying slice bytes won't be changed, you can use NoCopy to tell
|
||||
gopacket.NewPacket, and it'll use the passed-in slice itself.
|
||||
|
||||
// This channel returns new byte slices, each of which points to a new
|
||||
// memory location that's guaranteed immutable for the duration of the
|
||||
// packet.
|
||||
for data := range myByteSliceChannel {
|
||||
p := gopacket.NewPacket(data, layers.LayerTypeEthernet, gopacket.NoCopy)
|
||||
doSomethingWithPacket(p)
|
||||
}
|
||||
|
||||
The fastest method of decoding is to use both Lazy and NoCopy, but note from
|
||||
the many caveats above that for some implementations either or both may be
|
||||
dangerous.
|
||||
|
||||
|
||||
Pointers To Known Layers
|
||||
|
||||
During decoding, certain layers are stored in the packet as well-known
|
||||
layer types. For example, IPv4 and IPv6 are both considered NetworkLayer
|
||||
layers, while TCP and UDP are both TransportLayer layers. We support 4
|
||||
layers, corresponding to the 4 layers of the TCP/IP layering scheme (roughly
|
||||
anagalous to layers 2, 3, 4, and 7 of the OSI model). To access these,
|
||||
you can use the packet.LinkLayer, packet.NetworkLayer,
|
||||
packet.TransportLayer, and packet.ApplicationLayer functions. Each of
|
||||
these functions returns a corresponding interface
|
||||
(gopacket.{Link,Network,Transport,Application}Layer). The first three
|
||||
provide methods for getting src/dst addresses for that particular layer,
|
||||
while the final layer provides a Payload function to get payload data.
|
||||
This is helpful, for example, to get payloads for all packets regardless
|
||||
of their underlying data type:
|
||||
|
||||
// Get packets from some source
|
||||
for packet := range someSource {
|
||||
if app := packet.ApplicationLayer(); app != nil {
|
||||
if strings.Contains(string(app.Payload()), "magic string") {
|
||||
fmt.Println("Found magic string in a packet!")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
A particularly useful layer is ErrorLayer, which is set whenever there's
|
||||
an error parsing part of the packet.
|
||||
|
||||
packet := gopacket.NewPacket(myPacketData, layers.LayerTypeEthernet, gopacket.Default)
|
||||
if err := packet.ErrorLayer(); err != nil {
|
||||
fmt.Println("Error decoding some part of the packet:", err)
|
||||
}
|
||||
|
||||
Note that we don't return an error from NewPacket because we may have decoded
|
||||
a number of layers successfully before running into our erroneous layer. You
|
||||
may still be able to get your Ethernet and IPv4 layers correctly, even if
|
||||
your TCP layer is malformed.
|
||||
|
||||
|
||||
Flow And Endpoint
|
||||
|
||||
gopacket has two useful objects, Flow and Endpoint, for communicating in a protocol
|
||||
independent manner the fact that a packet is coming from A and going to B.
|
||||
The general layer types LinkLayer, NetworkLayer, and TransportLayer all provide
|
||||
methods for extracting their flow information, without worrying about the type
|
||||
of the underlying Layer.
|
||||
|
||||
A Flow is a simple object made up of a set of two Endpoints, one source and one
|
||||
destination. It details the sender and receiver of the Layer of the Packet.
|
||||
|
||||
An Endpoint is a hashable representation of a source or destination. For
|
||||
example, for LayerTypeIPv4, an Endpoint contains the IP address bytes for a v4
|
||||
IP packet. A Flow can be broken into Endpoints, and Endpoints can be combined
|
||||
into Flows:
|
||||
|
||||
packet := gopacket.NewPacket(myPacketData, layers.LayerTypeEthernet, gopacket.Lazy)
|
||||
netFlow := packet.NetworkLayer().NetworkFlow()
|
||||
src, dst := netFlow.Endpoints()
|
||||
reverseFlow := gopacket.NewFlow(dst, src)
|
||||
|
||||
Both Endpoint and Flow objects can be used as map keys, and the equality
|
||||
operator can compare them, so you can easily group together all packets
|
||||
based on endpoint criteria:
|
||||
|
||||
flows := map[gopacket.Endpoint]chan gopacket.Packet
|
||||
packet := gopacket.NewPacket(myPacketData, layers.LayerTypeEthernet, gopacket.Lazy)
|
||||
// Send all TCP packets to channels based on their destination port.
|
||||
if tcp := packet.Layer(layers.LayerTypeTCP); tcp != nil {
|
||||
flows[tcp.TransportFlow().Dst()] <- packet
|
||||
}
|
||||
// Look for all packets with the same source and destination network address
|
||||
if net := packet.NetworkLayer(); net != nil {
|
||||
src, dst := net.NetworkFlow().Endpoints()
|
||||
if src == dst {
|
||||
fmt.Println("Fishy packet has same network source and dst: %s", src)
|
||||
}
|
||||
}
|
||||
// Find all packets coming from UDP port 1000 to UDP port 500
|
||||
interestingFlow := gopacket.NewFlow(layers.NewUDPPortEndpoint(1000), layers.NewUDPPortEndpoint(500))
|
||||
if t := packet.NetworkLayer(); t != nil && t.TransportFlow() == interestingFlow {
|
||||
fmt.Println("Found that UDP flow I was looking for!")
|
||||
}
|
||||
|
||||
For load-balancing purposes, both Flow and Endpoint have FastHash() functions,
|
||||
which provide quick, non-cryptographic hashes of their contents. Of particular
|
||||
importance is the fact that Flow FastHash() is symmetric: A->B will have the same
|
||||
hash as B->A. An example usage could be:
|
||||
|
||||
channels := [8]chan gopacket.Packet
|
||||
for i := 0; i < 8; i++ {
|
||||
channels[i] = make(chan gopacket.Packet)
|
||||
go packetHandler(channels[i])
|
||||
}
|
||||
for packet := range getPackets() {
|
||||
if net := packet.NetworkLayer(); net != nil {
|
||||
channels[int(net.NetworkFlow().FastHash()) & 0x7] <- packet
|
||||
}
|
||||
}
|
||||
|
||||
This allows us to split up a packet stream while still making sure that each
|
||||
stream sees all packets for a flow (and its bidirectional opposite).
|
||||
|
||||
|
||||
Implementing Your Own Decoder
|
||||
|
||||
If your network has some strange encapsulation, you can implement your own
|
||||
decoder. In this example, we handle Ethernet packets which are encapsulated
|
||||
in a 4-byte header.
|
||||
|
||||
// Create a layer type, should be unique and high, so it doesn't conflict,
|
||||
// giving it a name and a decoder to use.
|
||||
var MyLayerType = gopacket.RegisterLayerType(12345, "MyLayerType", gopacket.DecodeFunc(decodeMyLayer))
|
||||
|
||||
// Implement my layer
|
||||
type MyLayer struct {
|
||||
StrangeHeader []byte
|
||||
payload []byte
|
||||
}
|
||||
func (m MyLayer) LayerType() LayerType { return MyLayerType }
|
||||
func (m MyLayer) LayerContents() []byte { return m.StrangeHeader }
|
||||
func (m MyLayer) LayerPayload() []byte { return m.payload }
|
||||
|
||||
// Now implement a decoder... this one strips off the first 4 bytes of the
|
||||
// packet.
|
||||
func decodeMyLayer(data []byte, p gopacket.PacketBuilder) error {
|
||||
// Create my layer
|
||||
p.AddLayer(&MyLayer{data[:4], data[4:]})
|
||||
// Determine how to handle the rest of the packet
|
||||
return p.NextDecoder(layers.LayerTypeEthernet)
|
||||
}
|
||||
|
||||
// Finally, decode your packets:
|
||||
p := gopacket.NewPacket(data, MyLayerType, gopacket.Lazy)
|
||||
|
||||
See the docs for Decoder and PacketBuilder for more details on how coding
|
||||
decoders works, or look at RegisterLayerType and RegisterEndpointType to see how
|
||||
to add layer/endpoint types to gopacket.
|
||||
|
||||
|
||||
Fast Decoding With DecodingLayerParser
|
||||
|
||||
TLDR: DecodingLayerParser takes about 10% of the time as NewPacket to decode
|
||||
packet data, but only for known packet stacks.
|
||||
|
||||
Basic decoding using gopacket.NewPacket or PacketSource.Packets is somewhat slow
|
||||
due to its need to allocate a new packet and every respective layer. It's very
|
||||
versatile and can handle all known layer types, but sometimes you really only
|
||||
care about a specific set of layers regardless, so that versatility is wasted.
|
||||
|
||||
DecodingLayerParser avoids memory allocation altogether by decoding packet
|
||||
layers directly into preallocated objects, which you can then reference to get
|
||||
the packet's information. A quick example:
|
||||
|
||||
func main() {
|
||||
var eth layers.Ethernet
|
||||
var ip4 layers.IPv4
|
||||
var ip6 layers.IPv6
|
||||
var tcp layers.TCP
|
||||
parser := gopacket.NewDecodingLayerParser(layers.LayerTypeEthernet, ð, &ip4, &ip6, &tcp)
|
||||
decoded := []gopacket.LayerType{}
|
||||
for packetData := range somehowGetPacketData() {
|
||||
err := parser.DecodeLayers(packetData, &decoded)
|
||||
for _, layerType := range decoded {
|
||||
switch layerType {
|
||||
case layers.LayerTypeIPv6:
|
||||
fmt.Println(" IP6 ", ip6.SrcIP, ip6.DstIP)
|
||||
case layers.LayerTypeIPv4:
|
||||
fmt.Println(" IP4 ", ip4.SrcIP, ip4.DstIP)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
The important thing to note here is that the parser is modifying the passed in
|
||||
layers (eth, ip4, ip6, tcp) instead of allocating new ones, thus greatly
|
||||
speeding up the decoding process. It's even branching based on layer type...
|
||||
it'll handle an (eth, ip4, tcp) or (eth, ip6, tcp) stack. However, it won't
|
||||
handle any other type... since no other decoders were passed in, an (eth, ip4,
|
||||
udp) stack will stop decoding after ip4, and only pass back [LayerTypeEthernet,
|
||||
LayerTypeIPv4] through the 'decoded' slice (along with an error saying it can't
|
||||
decode a UDP packet).
|
||||
|
||||
Unfortunately, not all layers can be used by DecodingLayerParser... only those
|
||||
implementing the DecodingLayer interface are usable. Also, it's possible to
|
||||
create DecodingLayers that are not themselves Layers... see
|
||||
layers.IPv6ExtensionSkipper for an example of this.
|
||||
|
||||
|
||||
Creating Packet Data
|
||||
|
||||
As well as offering the ability to decode packet data, gopacket will allow you
|
||||
to create packets from scratch, as well. A number of gopacket layers implement
|
||||
the SerializableLayer interface; these layers can be serialized to a []byte in
|
||||
the following manner:
|
||||
|
||||
ip := &layers.IPv4{
|
||||
SrcIP: net.IP{1, 2, 3, 4},
|
||||
DstIP: net.IP{5, 6, 7, 8},
|
||||
// etc...
|
||||
}
|
||||
buf := gopacket.NewSerializeBuffer()
|
||||
opts := gopacket.SerializeOptions{} // See SerializeOptions for more details.
|
||||
err := ip.SerializeTo(&buf, opts)
|
||||
if err != nil { panic(err) }
|
||||
fmt.Println(buf.Bytes()) // prints out a byte slice containing the serialized IPv4 layer.
|
||||
|
||||
SerializeTo PREPENDS the given layer onto the SerializeBuffer, and they treat
|
||||
the current buffer's Bytes() slice as the payload of the serializing layer.
|
||||
Therefore, you can serialize an entire packet by serializing a set of layers in
|
||||
reverse order (Payload, then TCP, then IP, then Ethernet, for example). The
|
||||
SerializeBuffer's SerializeLayers function is a helper that does exactly that.
|
||||
|
||||
To generate a (empty and useless, because no fields are set)
|
||||
Ethernet(IPv4(TCP(Payload))) packet, for example, you can run:
|
||||
|
||||
buf := gopacket.NewSerializeBuffer()
|
||||
opts := gopacket.SerializeOptions{}
|
||||
gopacket.SerializeLayers(buf, opts,
|
||||
&layers.Ethernet{},
|
||||
&layers.IPv4{},
|
||||
&layers.TCP{},
|
||||
gopacket.Payload([]byte{1, 2, 3, 4}))
|
||||
packetData := buf.Bytes()
|
||||
|
||||
A Final Note
|
||||
|
||||
If you use gopacket, you'll almost definitely want to make sure gopacket/layers
|
||||
is imported, since when imported it sets all the LayerType variables and fills
|
||||
in a lot of interesting variables/maps (DecodersByLayerName, etc). Therefore,
|
||||
it's recommended that even if you don't use any layers functions directly, you still import with:
|
||||
|
||||
import (
|
||||
_ "github.com/google/gopacket/layers"
|
||||
)
|
||||
*/
|
||||
package gopacket
|
@ -0,0 +1,236 @@
|
||||
// Copyright 2012 Google, Inc. All rights reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the LICENSE file in the root of the source
|
||||
// tree.
|
||||
|
||||
package gopacket
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// MaxEndpointSize determines the maximum size in bytes of an endpoint address.
|
||||
//
|
||||
// Endpoints/Flows have a problem: They need to be hashable. Therefore, they
|
||||
// can't use a byte slice. The two obvious choices are to use a string or a
|
||||
// byte array. Strings work great, but string creation requires memory
|
||||
// allocation, which can be slow. Arrays work great, but have a fixed size. We
|
||||
// originally used the former, now we've switched to the latter. Use of a fixed
|
||||
// byte-array doubles the speed of constructing a flow (due to not needing to
|
||||
// allocate). This is a huge increase... too much for us to pass up.
|
||||
//
|
||||
// The end result of this, though, is that an endpoint/flow can't be created
|
||||
// using more than MaxEndpointSize bytes per address.
|
||||
const MaxEndpointSize = 16
|
||||
|
||||
// Endpoint is the set of bytes used to address packets at various layers.
|
||||
// See LinkLayer, NetworkLayer, and TransportLayer specifications.
|
||||
// Endpoints are usable as map keys.
|
||||
type Endpoint struct {
|
||||
typ EndpointType
|
||||
len int
|
||||
raw [MaxEndpointSize]byte
|
||||
}
|
||||
|
||||
// EndpointType returns the endpoint type associated with this endpoint.
|
||||
func (a Endpoint) EndpointType() EndpointType { return a.typ }
|
||||
|
||||
// Raw returns the raw bytes of this endpoint. These aren't human-readable
|
||||
// most of the time, but they are faster than calling String.
|
||||
func (a Endpoint) Raw() []byte { return a.raw[:a.len] }
|
||||
|
||||
// LessThan provides a stable ordering for all endpoints. It sorts first based
|
||||
// on the EndpointType of an endpoint, then based on the raw bytes of that
|
||||
// endpoint.
|
||||
//
|
||||
// For some endpoints, the actual comparison may not make sense, however this
|
||||
// ordering does provide useful information for most Endpoint types.
|
||||
// Ordering is based first on endpoint type, then on raw endpoint bytes.
|
||||
// Endpoint bytes are sorted lexigraphically.
|
||||
func (a Endpoint) LessThan(b Endpoint) bool {
|
||||
return a.typ < b.typ || (a.typ == b.typ && bytes.Compare(a.raw[:a.len], b.raw[:b.len]) < 0)
|
||||
}
|
||||
|
||||
// fnvHash is used by our FastHash functions, and implements the FNV hash
|
||||
// created by Glenn Fowler, Landon Curt Noll, and Phong Vo.
|
||||
// See http://isthe.com/chongo/tech/comp/fnv/.
|
||||
func fnvHash(s []byte) (h uint64) {
|
||||
h = fnvBasis
|
||||
for i := 0; i < len(s); i++ {
|
||||
h ^= uint64(s[i])
|
||||
h *= fnvPrime
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
const fnvBasis = 14695981039346656037
|
||||
const fnvPrime = 1099511628211
|
||||
|
||||
// FastHash provides a quick hashing function for an endpoint, useful if you'd
|
||||
// like to split up endpoints by modulos or other load-balancing techniques.
|
||||
// It uses a variant of Fowler-Noll-Vo hashing.
|
||||
//
|
||||
// The output of FastHash is not guaranteed to remain the same through future
|
||||
// code revisions, so should not be used to key values in persistent storage.
|
||||
func (a Endpoint) FastHash() (h uint64) {
|
||||
h = fnvHash(a.raw[:a.len])
|
||||
h ^= uint64(a.typ)
|
||||
h *= fnvPrime
|
||||
return
|
||||
}
|
||||
|
||||
// NewEndpoint creates a new Endpoint object.
|
||||
//
|
||||
// The size of raw must be less than MaxEndpointSize, otherwise this function
|
||||
// will panic.
|
||||
func NewEndpoint(typ EndpointType, raw []byte) (e Endpoint) {
|
||||
e.len = len(raw)
|
||||
if e.len > MaxEndpointSize {
|
||||
panic("raw byte length greater than MaxEndpointSize")
|
||||
}
|
||||
e.typ = typ
|
||||
copy(e.raw[:], raw)
|
||||
return
|
||||
}
|
||||
|
||||
// EndpointTypeMetadata is used to register a new endpoint type.
|
||||
type EndpointTypeMetadata struct {
|
||||
// Name is the string returned by an EndpointType's String function.
|
||||
Name string
|
||||
// Formatter is called from an Endpoint's String function to format the raw
|
||||
// bytes in an Endpoint into a human-readable string.
|
||||
Formatter func([]byte) string
|
||||
}
|
||||
|
||||
// EndpointType is the type of a gopacket Endpoint. This type determines how
|
||||
// the bytes stored in the endpoint should be interpreted.
|
||||
type EndpointType int64
|
||||
|
||||
var endpointTypes = map[EndpointType]EndpointTypeMetadata{}
|
||||
|
||||
// RegisterEndpointType creates a new EndpointType and registers it globally.
|
||||
// It MUST be passed a unique number, or it will panic. Numbers 0-999 are
|
||||
// reserved for gopacket's use.
|
||||
func RegisterEndpointType(num int, meta EndpointTypeMetadata) EndpointType {
|
||||
t := EndpointType(num)
|
||||
if _, ok := endpointTypes[t]; ok {
|
||||
panic("Endpoint type number already in use")
|
||||
}
|
||||
endpointTypes[t] = meta
|
||||
return t
|
||||
}
|
||||
|
||||
func (e EndpointType) String() string {
|
||||
if t, ok := endpointTypes[e]; ok {
|
||||
return t.Name
|
||||
}
|
||||
return strconv.Itoa(int(e))
|
||||
}
|
||||
|
||||
func (a Endpoint) String() string {
|
||||
if t, ok := endpointTypes[a.typ]; ok && t.Formatter != nil {
|
||||
return t.Formatter(a.raw[:a.len])
|
||||
}
|
||||
return fmt.Sprintf("%v:%v", a.typ, a.raw)
|
||||
}
|
||||
|
||||
// Flow represents the direction of traffic for a packet layer, as a source and destination Endpoint.
|
||||
// Flows are usable as map keys.
|
||||
type Flow struct {
|
||||
typ EndpointType
|
||||
slen, dlen int
|
||||
src, dst [MaxEndpointSize]byte
|
||||
}
|
||||
|
||||
// FlowFromEndpoints creates a new flow by pasting together two endpoints.
|
||||
// The endpoints must have the same EndpointType, or this function will return
|
||||
// an error.
|
||||
func FlowFromEndpoints(src, dst Endpoint) (_ Flow, err error) {
|
||||
if src.typ != dst.typ {
|
||||
err = fmt.Errorf("Mismatched endpoint types: %v->%v", src.typ, dst.typ)
|
||||
return
|
||||
}
|
||||
return Flow{src.typ, src.len, dst.len, src.raw, dst.raw}, nil
|
||||
}
|
||||
|
||||
// FastHash provides a quick hashing function for a flow, useful if you'd
|
||||
// like to split up flows by modulos or other load-balancing techniques.
|
||||
// It uses a variant of Fowler-Noll-Vo hashing, and is guaranteed to collide
|
||||
// with its reverse flow. IE: the flow A->B will have the same hash as the flow
|
||||
// B->A.
|
||||
//
|
||||
// The output of FastHash is not guaranteed to remain the same through future
|
||||
// code revisions, so should not be used to key values in persistent storage.
|
||||
func (f Flow) FastHash() (h uint64) {
|
||||
// This combination must be commutative. We don't use ^, since that would
|
||||
// give the same hash for all A->A flows.
|
||||
h = fnvHash(f.src[:f.slen]) + fnvHash(f.dst[:f.dlen])
|
||||
h ^= uint64(f.typ)
|
||||
h *= fnvPrime
|
||||
return
|
||||
}
|
||||
|
||||
// String returns a human-readable representation of this flow, in the form
|
||||
// "Src->Dst"
|
||||
func (f Flow) String() string {
|
||||
s, d := f.Endpoints()
|
||||
return fmt.Sprintf("%v->%v", s, d)
|
||||
}
|
||||
|
||||
// EndpointType returns the EndpointType for this Flow.
|
||||
func (f Flow) EndpointType() EndpointType {
|
||||
return f.typ
|
||||
}
|
||||
|
||||
// Endpoints returns the two Endpoints for this flow.
|
||||
func (f Flow) Endpoints() (src, dst Endpoint) {
|
||||
return Endpoint{f.typ, f.slen, f.src}, Endpoint{f.typ, f.dlen, f.dst}
|
||||
}
|
||||
|
||||
// Src returns the source Endpoint for this flow.
|
||||
func (f Flow) Src() (src Endpoint) {
|
||||
src, _ = f.Endpoints()
|
||||
return
|
||||
}
|
||||
|
||||
// Dst returns the destination Endpoint for this flow.
|
||||
func (f Flow) Dst() (dst Endpoint) {
|
||||
_, dst = f.Endpoints()
|
||||
return
|
||||
}
|
||||
|
||||
// Reverse returns a new flow with endpoints reversed.
|
||||
func (f Flow) Reverse() Flow {
|
||||
return Flow{f.typ, f.dlen, f.slen, f.dst, f.src}
|
||||
}
|
||||
|
||||
// NewFlow creates a new flow.
|
||||
//
|
||||
// src and dst must have length <= MaxEndpointSize, otherwise NewFlow will
|
||||
// panic.
|
||||
func NewFlow(t EndpointType, src, dst []byte) (f Flow) {
|
||||
f.slen = len(src)
|
||||
f.dlen = len(dst)
|
||||
if f.slen > MaxEndpointSize || f.dlen > MaxEndpointSize {
|
||||
panic("flow raw byte length greater than MaxEndpointSize")
|
||||
}
|
||||
f.typ = t
|
||||
copy(f.src[:], src)
|
||||
copy(f.dst[:], dst)
|
||||
return
|
||||
}
|
||||
|
||||
// EndpointInvalid is an endpoint type used for invalid endpoints, IE endpoints
|
||||
// that are specified incorrectly during creation.
|
||||
var EndpointInvalid = RegisterEndpointType(0, EndpointTypeMetadata{Name: "invalid", Formatter: func(b []byte) string {
|
||||
return fmt.Sprintf("%v", b)
|
||||
}})
|
||||
|
||||
// InvalidEndpoint is a singleton Endpoint of type EndpointInvalid.
|
||||
var InvalidEndpoint = NewEndpoint(EndpointInvalid, nil)
|
||||
|
||||
// InvalidFlow is a singleton Flow of type EndpointInvalid.
|
||||
var InvalidFlow = NewFlow(EndpointInvalid, nil, nil)
|
@ -0,0 +1,278 @@
|
||||
#!/bin/bash
|
||||
# Copyright 2012 Google, Inc. All rights reserved.
|
||||
|
||||
# This script provides a simple way to run benchmarks against previous code and
|
||||
# keep a log of how benchmarks change over time. When used with the --benchmark
|
||||
# flag, it runs benchmarks from the current code and from the last commit run
|
||||
# with --benchmark, then stores the results in the git commit description. We
|
||||
# rerun the old benchmarks along with the new ones, since there's no guarantee
|
||||
# that git commits will happen on the same machine, so machine differences could
|
||||
# cause wildly inaccurate results.
|
||||
#
|
||||
# If you're making changes to 'gopacket' which could cause performance changes,
|
||||
# you may be requested to use this commit script to make sure your changes don't
|
||||
# have large detrimental effects (or to show off how awesome your performance
|
||||
# improvements are).
|
||||
#
|
||||
# If not run with the --benchmark flag, this script is still very useful... it
|
||||
# makes sure all the correct go formatting, building, and testing work as
|
||||
# expected.
|
||||
|
||||
function Usage {
|
||||
cat <<EOF
|
||||
USAGE: $0 [--benchmark regexp] [--root] [--gen] <git commit flags...>
|
||||
|
||||
--benchmark: Run benchmark comparisons against last benchmark'd commit
|
||||
--root: Run tests that require root priviledges
|
||||
--gen: Generate code for MACs/ports by pulling down external data
|
||||
|
||||
Note, some 'git commit' flags are necessary, if all else fails, pass in -a
|
||||
EOF
|
||||
exit 1
|
||||
}
|
||||
|
||||
BENCH=""
|
||||
GEN=""
|
||||
ROOT=""
|
||||
while [ ! -z "$1" ]; do
|
||||
case "$1" in
|
||||
"--benchmark")
|
||||
BENCH="$2"
|
||||
shift
|
||||
shift
|
||||
;;
|
||||
"--gen")
|
||||
GEN="yes"
|
||||
shift
|
||||
;;
|
||||
"--root")
|
||||
ROOT="yes"
|
||||
shift
|
||||
;;
|
||||
"--help")
|
||||
Usage
|
||||
;;
|
||||
"-h")
|
||||
Usage
|
||||
;;
|
||||
"help")
|
||||
Usage
|
||||
;;
|
||||
*)
|
||||
break
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
function Root {
|
||||
if [ ! -z "$ROOT" ]; then
|
||||
local exec="$1"
|
||||
# Some folks (like me) keep source code in places inaccessible by root (like
|
||||
# NFS), so to make sure things run smoothly we copy them to a /tmp location.
|
||||
local tmpfile="$(mktemp -t gopacket_XXXXXXXX)"
|
||||
echo "Running root test executable $exec as $tmpfile"
|
||||
cp "$exec" "$tmpfile"
|
||||
chmod a+x "$tmpfile"
|
||||
shift
|
||||
sudo "$tmpfile" "$@"
|
||||
fi
|
||||
}
|
||||
|
||||
if [ "$#" -eq "0" ]; then
|
||||
Usage
|
||||
fi
|
||||
|
||||
cd $(dirname $0)
|
||||
|
||||
# Check for copyright notices.
|
||||
for filename in $(find ./ -type f -name '*.go'); do
|
||||
if ! head -n 1 "$filename" | grep -q Copyright; then
|
||||
echo "File '$filename' may not have copyright notice"
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
set -e
|
||||
set -x
|
||||
|
||||
if [ ! -z "$ROOT" ]; then
|
||||
echo "Running SUDO to get root priviledges for root tests"
|
||||
sudo echo "have root"
|
||||
fi
|
||||
|
||||
if [ ! -z "$GEN" ]; then
|
||||
pushd macs
|
||||
go run gen.go | gofmt > valid_mac_prefixes.go
|
||||
popd
|
||||
pushd layers
|
||||
go run gen.go | gofmt > iana_ports.go
|
||||
popd
|
||||
fi
|
||||
|
||||
# Make sure everything is formatted, compiles, and tests pass.
|
||||
go fmt ./...
|
||||
go test -i ./... 2>/dev/null >/dev/null || true
|
||||
go test
|
||||
go build
|
||||
pushd examples/bytediff
|
||||
go build
|
||||
popd
|
||||
if [ -f /usr/include/pcap.h ]; then
|
||||
pushd pcap
|
||||
go test ./...
|
||||
go build ./...
|
||||
go build pcap_tester.go
|
||||
Root pcap_tester --mode=basic
|
||||
Root pcap_tester --mode=filtered
|
||||
Root pcap_tester --mode=timestamp || echo "You might not support timestamp sources"
|
||||
popd
|
||||
pushd examples/pcapdump
|
||||
go build
|
||||
popd
|
||||
pushd examples/arpscan
|
||||
go build
|
||||
popd
|
||||
pushd examples/bidirectional
|
||||
go build
|
||||
popd
|
||||
pushd examples/synscan
|
||||
go build
|
||||
popd
|
||||
pushd examples/httpassembly
|
||||
go build
|
||||
popd
|
||||
pushd examples/statsassembly
|
||||
go build
|
||||
popd
|
||||
fi
|
||||
pushd macs
|
||||
go test ./...
|
||||
gofmt -w gen.go
|
||||
go build gen.go
|
||||
popd
|
||||
pushd tcpassembly
|
||||
go test ./...
|
||||
popd
|
||||
pushd reassembly
|
||||
go test ./...
|
||||
popd
|
||||
pushd layers
|
||||
gofmt -w gen.go
|
||||
go build gen.go
|
||||
go test ./...
|
||||
popd
|
||||
pushd pcapgo
|
||||
go test ./...
|
||||
go build ./...
|
||||
popd
|
||||
if [ -f /usr/include/linux/if_packet.h ]; then
|
||||
if grep -q TPACKET_V3 /usr/include/linux/if_packet.h; then
|
||||
pushd afpacket
|
||||
go build ./...
|
||||
go test ./...
|
||||
popd
|
||||
fi
|
||||
fi
|
||||
if [ -f /usr/include/pfring.h ]; then
|
||||
pushd pfring
|
||||
go test ./...
|
||||
go build ./...
|
||||
popd
|
||||
pushd examples/pfdump
|
||||
go build
|
||||
popd
|
||||
fi
|
||||
|
||||
for travis_script in `ls .travis.*.sh`; do
|
||||
./$travis_script
|
||||
done
|
||||
|
||||
# Run our initial commit
|
||||
git commit "$@"
|
||||
|
||||
if [ -z "$BENCH" ]; then
|
||||
set +x
|
||||
echo "We're not benchmarking and we've committed... we're done!"
|
||||
exit
|
||||
fi
|
||||
|
||||
### If we get here, we want to run benchmarks from current commit, and compare
|
||||
### then to benchmarks from the last --benchmark commit.
|
||||
|
||||
# Get our current branch.
|
||||
BRANCH="$(git branch | grep '^*' | awk '{print $2}')"
|
||||
|
||||
# File we're going to build our commit description in.
|
||||
COMMIT_FILE="$(mktemp /tmp/tmp.XXXXXXXX)"
|
||||
|
||||
# Add the word "BENCH" to the start of the git commit.
|
||||
echo -n "BENCH " > $COMMIT_FILE
|
||||
|
||||
# Get the current description... there must be an easier way.
|
||||
git log -n 1 | grep '^ ' | sed 's/^ //' >> $COMMIT_FILE
|
||||
|
||||
# Get the commit sha for the last benchmark commit
|
||||
PREV=$(git log -n 1 --grep='BENCHMARK_MARKER_DO_NOT_CHANGE' | head -n 1 | awk '{print $2}')
|
||||
|
||||
## Run current benchmarks
|
||||
|
||||
cat >> $COMMIT_FILE <<EOF
|
||||
|
||||
|
||||
----------------------------------------------------------
|
||||
BENCHMARK_MARKER_DO_NOT_CHANGE
|
||||
----------------------------------------------------------
|
||||
|
||||
Go version $(go version)
|
||||
|
||||
|
||||
TEST BENCHMARKS "$BENCH"
|
||||
EOF
|
||||
# go seems to have trouble with 'go test --bench=. ./...'
|
||||
go test --test.bench="$BENCH" 2>&1 | tee -a $COMMIT_FILE
|
||||
pushd layers
|
||||
go test --test.bench="$BENCH" 2>&1 | tee -a $COMMIT_FILE
|
||||
popd
|
||||
cat >> $COMMIT_FILE <<EOF
|
||||
|
||||
|
||||
PCAP BENCHMARK
|
||||
EOF
|
||||
if [ "$BENCH" -eq ".*" ]; then
|
||||
go run pcap/gopacket_benchmark/*.go 2>&1 | tee -a $COMMIT_FILE
|
||||
fi
|
||||
|
||||
|
||||
|
||||
## Reset to last benchmark commit, run benchmarks
|
||||
|
||||
git checkout $PREV
|
||||
|
||||
cat >> $COMMIT_FILE <<EOF
|
||||
----------------------------------------------------------
|
||||
BENCHMARKING AGAINST COMMIT $PREV
|
||||
----------------------------------------------------------
|
||||
|
||||
|
||||
OLD TEST BENCHMARKS
|
||||
EOF
|
||||
# go seems to have trouble with 'go test --bench=. ./...'
|
||||
go test --test.bench="$BENCH" 2>&1 | tee -a $COMMIT_FILE
|
||||
pushd layers
|
||||
go test --test.bench="$BENCH" 2>&1 | tee -a $COMMIT_FILE
|
||||
popd
|
||||
cat >> $COMMIT_FILE <<EOF
|
||||
|
||||
|
||||
OLD PCAP BENCHMARK
|
||||
EOF
|
||||
if [ "$BENCH" -eq ".*" ]; then
|
||||
go run pcap/gopacket_benchmark/*.go 2>&1 | tee -a $COMMIT_FILE
|
||||
fi
|
||||
|
||||
|
||||
|
||||
## Reset back to the most recent commit, edit the commit message by appending
|
||||
## benchmark results.
|
||||
git checkout $BRANCH
|
||||
git commit --amend -F $COMMIT_FILE
|
@ -0,0 +1,107 @@
|
||||
// Copyright 2012 Google, Inc. All rights reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the LICENSE file in the root of the source
|
||||
// tree.
|
||||
|
||||
package gopacket
|
||||
|
||||
// LayerClass is a set of LayerTypes, used for grabbing one of a number of
|
||||
// different types from a packet.
|
||||
type LayerClass interface {
|
||||
// Contains returns true if the given layer type should be considered part
|
||||
// of this layer class.
|
||||
Contains(LayerType) bool
|
||||
// LayerTypes returns the set of all layer types in this layer class.
|
||||
// Note that this may not be a fast operation on all LayerClass
|
||||
// implementations.
|
||||
LayerTypes() []LayerType
|
||||
}
|
||||
|
||||
// Contains implements LayerClass.
|
||||
func (l LayerType) Contains(a LayerType) bool {
|
||||
return l == a
|
||||
}
|
||||
|
||||
// LayerTypes implements LayerClass.
|
||||
func (l LayerType) LayerTypes() []LayerType {
|
||||
return []LayerType{l}
|
||||
}
|
||||
|
||||
// LayerClassSlice implements a LayerClass with a slice.
|
||||
type LayerClassSlice []bool
|
||||
|
||||
// Contains returns true if the given layer type should be considered part
|
||||
// of this layer class.
|
||||
func (s LayerClassSlice) Contains(t LayerType) bool {
|
||||
return int(t) < len(s) && s[t]
|
||||
}
|
||||
|
||||
// LayerTypes returns all layer types in this LayerClassSlice.
|
||||
// Because of LayerClassSlice's implementation, this could be quite slow.
|
||||
func (s LayerClassSlice) LayerTypes() (all []LayerType) {
|
||||
for i := 0; i < len(s); i++ {
|
||||
if s[i] {
|
||||
all = append(all, LayerType(i))
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// NewLayerClassSlice creates a new LayerClassSlice by creating a slice of
|
||||
// size max(types) and setting slice[t] to true for each type t. Note, if
|
||||
// you implement your own LayerType and give it a high value, this WILL create
|
||||
// a very large slice.
|
||||
func NewLayerClassSlice(types []LayerType) LayerClassSlice {
|
||||
var max LayerType
|
||||
for _, typ := range types {
|
||||
if typ > max {
|
||||
max = typ
|
||||
}
|
||||
}
|
||||
t := make([]bool, int(max+1))
|
||||
for _, typ := range types {
|
||||
t[typ] = true
|
||||
}
|
||||
return t
|
||||
}
|
||||
|
||||
// LayerClassMap implements a LayerClass with a map.
|
||||
type LayerClassMap map[LayerType]bool
|
||||
|
||||
// Contains returns true if the given layer type should be considered part
|
||||
// of this layer class.
|
||||
func (m LayerClassMap) Contains(t LayerType) bool {
|
||||
return m[t]
|
||||
}
|
||||
|
||||
// LayerTypes returns all layer types in this LayerClassMap.
|
||||
func (m LayerClassMap) LayerTypes() (all []LayerType) {
|
||||
for t := range m {
|
||||
all = append(all, t)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// NewLayerClassMap creates a LayerClassMap and sets map[t] to true for each
|
||||
// type in types.
|
||||
func NewLayerClassMap(types []LayerType) LayerClassMap {
|
||||
m := LayerClassMap{}
|
||||
for _, typ := range types {
|
||||
m[typ] = true
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
// NewLayerClass creates a LayerClass, attempting to be smart about which type
|
||||
// it creates based on which types are passed in.
|
||||
func NewLayerClass(types []LayerType) LayerClass {
|
||||
for _, typ := range types {
|
||||
if typ > maxLayerType {
|
||||
// NewLayerClassSlice could create a very large object, so instead create
|
||||
// a map.
|
||||
return NewLayerClassMap(types)
|
||||
}
|
||||
}
|
||||
return NewLayerClassSlice(types)
|
||||
}
|
@ -0,0 +1,40 @@
|
||||
arp.go
|
||||
base.go
|
||||
base_test.go
|
||||
cdp.go
|
||||
ctp.go
|
||||
decode_test.go
|
||||
dhcp_test.go
|
||||
dhcpv4.go
|
||||
dns.go
|
||||
dns_test.go
|
||||
doc.go
|
||||
dot11_test.go
|
||||
dot1q.go
|
||||
dot1q_test.go
|
||||
eapol.go
|
||||
etherip.go
|
||||
fddi.go
|
||||
gen.go
|
||||
gre.go
|
||||
gre_test.go
|
||||
iana_ports.go
|
||||
icmp6_test.go
|
||||
igmp_test.go
|
||||
ip4_test.go
|
||||
ipsec.go
|
||||
ipsec_test.go
|
||||
loopback.go
|
||||
mpls_test.go
|
||||
ntp_test.go
|
||||
ports.go
|
||||
ppp.go
|
||||
prism_test.go
|
||||
radiotap_test.go
|
||||
sflow_test.go
|
||||
tcp_test.go
|
||||
udp_test.go
|
||||
usb_test.go
|
||||
vrrp_test.go
|
||||
vxlan.go
|
||||
vxlan_test.go
|
@ -0,0 +1,109 @@
|
||||
// Copyright 2012 Google, Inc. All rights reserved.
|
||||
// Copyright 2009-2011 Andreas Krennmair. 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"
|
||||
)
|
||||
|
||||
// Potential values for ARP.Operation.
|
||||
const (
|
||||
ARPRequest = 1
|
||||
ARPReply = 2
|
||||
)
|
||||
|
||||
// ARP is a ARP packet header.
|
||||
type ARP struct {
|
||||
BaseLayer
|
||||
AddrType LinkType
|
||||
Protocol EthernetType
|
||||
HwAddressSize uint8
|
||||
ProtAddressSize uint8
|
||||
Operation uint16
|
||||
SourceHwAddress []byte
|
||||
SourceProtAddress []byte
|
||||
DstHwAddress []byte
|
||||
DstProtAddress []byte
|
||||
}
|
||||
|
||||
// LayerType returns LayerTypeARP
|
||||
func (arp *ARP) LayerType() gopacket.LayerType { return LayerTypeARP }
|
||||
|
||||
// DecodeFromBytes decodes the given bytes into this layer.
|
||||
func (arp *ARP) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
|
||||
arp.AddrType = LinkType(binary.BigEndian.Uint16(data[0:2]))
|
||||
arp.Protocol = EthernetType(binary.BigEndian.Uint16(data[2:4]))
|
||||
arp.HwAddressSize = data[4]
|
||||
arp.ProtAddressSize = data[5]
|
||||
arp.Operation = binary.BigEndian.Uint16(data[6:8])
|
||||
arp.SourceHwAddress = data[8 : 8+arp.HwAddressSize]
|
||||
arp.SourceProtAddress = data[8+arp.HwAddressSize : 8+arp.HwAddressSize+arp.ProtAddressSize]
|
||||
arp.DstHwAddress = data[8+arp.HwAddressSize+arp.ProtAddressSize : 8+2*arp.HwAddressSize+arp.ProtAddressSize]
|
||||
arp.DstProtAddress = data[8+2*arp.HwAddressSize+arp.ProtAddressSize : 8+2*arp.HwAddressSize+2*arp.ProtAddressSize]
|
||||
|
||||
arpLength := 8 + 2*arp.HwAddressSize + 2*arp.ProtAddressSize
|
||||
arp.Contents = data[:arpLength]
|
||||
arp.Payload = data[arpLength:]
|
||||
return nil
|
||||
}
|
||||
|
||||
// SerializeTo writes the serialized form of this layer into the
|
||||
// SerializationBuffer, implementing gopacket.SerializableLayer.
|
||||
// See the docs for gopacket.SerializableLayer for more info.
|
||||
func (arp *ARP) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
|
||||
size := 8 + len(arp.SourceHwAddress) + len(arp.SourceProtAddress) + len(arp.DstHwAddress) + len(arp.DstProtAddress)
|
||||
bytes, err := b.PrependBytes(size)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if opts.FixLengths {
|
||||
if len(arp.SourceHwAddress) != len(arp.DstHwAddress) {
|
||||
return errors.New("mismatched hardware address sizes")
|
||||
}
|
||||
arp.HwAddressSize = uint8(len(arp.SourceHwAddress))
|
||||
if len(arp.SourceProtAddress) != len(arp.DstProtAddress) {
|
||||
return errors.New("mismatched prot address sizes")
|
||||
}
|
||||
arp.ProtAddressSize = uint8(len(arp.SourceProtAddress))
|
||||
}
|
||||
binary.BigEndian.PutUint16(bytes, uint16(arp.AddrType))
|
||||
binary.BigEndian.PutUint16(bytes[2:], uint16(arp.Protocol))
|
||||
bytes[4] = arp.HwAddressSize
|
||||
bytes[5] = arp.ProtAddressSize
|
||||
binary.BigEndian.PutUint16(bytes[6:], arp.Operation)
|
||||
start := 8
|
||||
for _, addr := range [][]byte{
|
||||
arp.SourceHwAddress,
|
||||
arp.SourceProtAddress,
|
||||
arp.DstHwAddress,
|
||||
arp.DstProtAddress,
|
||||
} {
|
||||
copy(bytes[start:], addr)
|
||||
start += len(addr)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// CanDecode returns the set of layer types that this DecodingLayer can decode.
|
||||
func (arp *ARP) CanDecode() gopacket.LayerClass {
|
||||
return LayerTypeARP
|
||||
}
|
||||
|
||||
// NextLayerType returns the layer type contained by this DecodingLayer.
|
||||
func (arp *ARP) NextLayerType() gopacket.LayerType {
|
||||
return gopacket.LayerTypePayload
|
||||
}
|
||||
|
||||
func decodeARP(data []byte, p gopacket.PacketBuilder) error {
|
||||
|
||||
arp := &ARP{}
|
||||
return decodingLayerDecoder(arp, data, p)
|
||||
}
|
@ -0,0 +1,52 @@
|
||||
// Copyright 2012 Google, Inc. All rights reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the LICENSE file in the root of the source
|
||||
// tree.
|
||||
|
||||
package layers
|
||||
|
||||
import (
|
||||
"github.com/google/gopacket"
|
||||
)
|
||||
|
||||
// BaseLayer is a convenience struct which implements the LayerData and
|
||||
// LayerPayload functions of the Layer interface.
|
||||
type BaseLayer struct {
|
||||
// Contents is the set of bytes that make up this layer. IE: for an
|
||||
// Ethernet packet, this would be the set of bytes making up the
|
||||
// Ethernet frame.
|
||||
Contents []byte
|
||||
// Payload is the set of bytes contained by (but not part of) this
|
||||
// Layer. Again, to take Ethernet as an example, this would be the
|
||||
// set of bytes encapsulated by the Ethernet protocol.
|
||||
Payload []byte
|
||||
}
|
||||
|
||||
// LayerContents returns the bytes of the packet layer.
|
||||
func (b *BaseLayer) LayerContents() []byte { return b.Contents }
|
||||
|
||||
// LayerPayload returns the bytes contained within the packet layer.
|
||||
func (b *BaseLayer) LayerPayload() []byte { return b.Payload }
|
||||
|
||||
type layerDecodingLayer interface {
|
||||
gopacket.Layer
|
||||
DecodeFromBytes([]byte, gopacket.DecodeFeedback) error
|
||||
NextLayerType() gopacket.LayerType
|
||||
}
|
||||
|
||||
func decodingLayerDecoder(d layerDecodingLayer, data []byte, p gopacket.PacketBuilder) error {
|
||||
err := d.DecodeFromBytes(data, p)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
p.AddLayer(d)
|
||||
next := d.NextLayerType()
|
||||
if next == gopacket.LayerTypeZero {
|
||||
return nil
|
||||
}
|
||||
return p.NextDecoder(next)
|
||||
}
|
||||
|
||||
// hacky way to zero out memory... there must be a better way?
|
||||
var lotsOfZeros [1024]byte
|
@ -0,0 +1,651 @@
|
||||
// Copyright 2012 Google, Inc. All rights reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the LICENSE file in the root of the source
|
||||
// tree.
|
||||
|
||||
// Enum types courtesy of...
|
||||
// http://search.cpan.org/~mchapman/Net-CDP-0.09/lib/Net/CDP.pm
|
||||
// https://code.google.com/p/ladvd/
|
||||
// http://anonsvn.wireshark.org/viewvc/releases/wireshark-1.8.6/epan/dissectors/packet-cdp.c
|
||||
|
||||
package layers
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"net"
|
||||
|
||||
"github.com/google/gopacket"
|
||||
)
|
||||
|
||||
// CDPTLVType is the type of each TLV value in a CiscoDiscovery packet.
|
||||
type CDPTLVType uint16
|
||||
|
||||
// CDPTLVType values.
|
||||
const (
|
||||
CDPTLVDevID CDPTLVType = 0x0001
|
||||
CDPTLVAddress CDPTLVType = 0x0002
|
||||
CDPTLVPortID CDPTLVType = 0x0003
|
||||
CDPTLVCapabilities CDPTLVType = 0x0004
|
||||
CDPTLVVersion CDPTLVType = 0x0005
|
||||
CDPTLVPlatform CDPTLVType = 0x0006
|
||||
CDPTLVIPPrefix CDPTLVType = 0x0007
|
||||
CDPTLVHello CDPTLVType = 0x0008
|
||||
CDPTLVVTPDomain CDPTLVType = 0x0009
|
||||
CDPTLVNativeVLAN CDPTLVType = 0x000a
|
||||
CDPTLVFullDuplex CDPTLVType = 0x000b
|
||||
CDPTLVVLANReply CDPTLVType = 0x000e
|
||||
CDPTLVVLANQuery CDPTLVType = 0x000f
|
||||
CDPTLVPower CDPTLVType = 0x0010
|
||||
CDPTLVMTU CDPTLVType = 0x0011
|
||||
CDPTLVExtendedTrust CDPTLVType = 0x0012
|
||||
CDPTLVUntrustedCOS CDPTLVType = 0x0013
|
||||
CDPTLVSysName CDPTLVType = 0x0014
|
||||
CDPTLVSysOID CDPTLVType = 0x0015
|
||||
CDPTLVMgmtAddresses CDPTLVType = 0x0016
|
||||
CDPTLVLocation CDPTLVType = 0x0017
|
||||
CDPTLVExternalPortID CDPTLVType = 0x0018
|
||||
CDPTLVPowerRequested CDPTLVType = 0x0019
|
||||
CDPTLVPowerAvailable CDPTLVType = 0x001a
|
||||
CDPTLVPortUnidirectional CDPTLVType = 0x001b
|
||||
CDPTLVEnergyWise CDPTLVType = 0x001d
|
||||
CDPTLVSparePairPOE CDPTLVType = 0x001f
|
||||
)
|
||||
|
||||
// CiscoDiscoveryValue is a TLV value inside a CiscoDiscovery packet layer.
|
||||
type CiscoDiscoveryValue struct {
|
||||
Type CDPTLVType
|
||||
Length uint16
|
||||
Value []byte
|
||||
}
|
||||
|
||||
// CiscoDiscovery is a packet layer containing the Cisco Discovery Protocol.
|
||||
// See http://www.cisco.com/univercd/cc/td/doc/product/lan/trsrb/frames.htm#31885
|
||||
type CiscoDiscovery struct {
|
||||
BaseLayer
|
||||
Version byte
|
||||
TTL byte
|
||||
Checksum uint16
|
||||
Values []CiscoDiscoveryValue
|
||||
}
|
||||
|
||||
// CDPCapability is the set of capabilities advertised by a CDP device.
|
||||
type CDPCapability uint32
|
||||
|
||||
// CDPCapability values.
|
||||
const (
|
||||
CDPCapMaskRouter CDPCapability = 0x0001
|
||||
CDPCapMaskTBBridge CDPCapability = 0x0002
|
||||
CDPCapMaskSPBridge CDPCapability = 0x0004
|
||||
CDPCapMaskSwitch CDPCapability = 0x0008
|
||||
CDPCapMaskHost CDPCapability = 0x0010
|
||||
CDPCapMaskIGMPFilter CDPCapability = 0x0020
|
||||
CDPCapMaskRepeater CDPCapability = 0x0040
|
||||
CDPCapMaskPhone CDPCapability = 0x0080
|
||||
CDPCapMaskRemote CDPCapability = 0x0100
|
||||
)
|
||||
|
||||
// CDPCapabilities represents the capabilities of a device
|
||||
type CDPCapabilities struct {
|
||||
L3Router bool
|
||||
TBBridge bool
|
||||
SPBridge bool
|
||||
L2Switch bool
|
||||
IsHost bool
|
||||
IGMPFilter bool
|
||||
L1Repeater bool
|
||||
IsPhone bool
|
||||
RemotelyManaged bool
|
||||
}
|
||||
|
||||
// CDP Power-over-Ethernet values.
|
||||
const (
|
||||
CDPPoEFourWire byte = 0x01
|
||||
CDPPoEPDArch byte = 0x02
|
||||
CDPPoEPDRequest byte = 0x04
|
||||
CDPPoEPSE byte = 0x08
|
||||
)
|
||||
|
||||
// CDPSparePairPoE provides information on PoE.
|
||||
type CDPSparePairPoE struct {
|
||||
PSEFourWire bool // Supported / Not supported
|
||||
PDArchShared bool // Shared / Independent
|
||||
PDRequestOn bool // On / Off
|
||||
PSEOn bool // On / Off
|
||||
}
|
||||
|
||||
// CDPVLANDialogue encapsulates a VLAN Query/Reply
|
||||
type CDPVLANDialogue struct {
|
||||
ID uint8
|
||||
VLAN uint16
|
||||
}
|
||||
|
||||
// CDPPowerDialogue encapsulates a Power Query/Reply
|
||||
type CDPPowerDialogue struct {
|
||||
ID uint16
|
||||
MgmtID uint16
|
||||
Values []uint32
|
||||
}
|
||||
|
||||
// CDPLocation provides location information for a CDP device.
|
||||
type CDPLocation struct {
|
||||
Type uint8 // Undocumented
|
||||
Location string
|
||||
}
|
||||
|
||||
// CDPHello is a Cisco Hello message (undocumented, hence the "Unknown" fields)
|
||||
type CDPHello struct {
|
||||
OUI []byte
|
||||
ProtocolID uint16
|
||||
ClusterMaster net.IP
|
||||
Unknown1 net.IP
|
||||
Version byte
|
||||
SubVersion byte
|
||||
Status byte
|
||||
Unknown2 byte
|
||||
ClusterCommander net.HardwareAddr
|
||||
SwitchMAC net.HardwareAddr
|
||||
Unknown3 byte
|
||||
ManagementVLAN uint16
|
||||
}
|
||||
|
||||
// CDPEnergyWiseSubtype is used within CDP to define TLV values.
|
||||
type CDPEnergyWiseSubtype uint32
|
||||
|
||||
// CDPEnergyWiseSubtype values.
|
||||
const (
|
||||
CDPEnergyWiseRole CDPEnergyWiseSubtype = 0x00000007
|
||||
CDPEnergyWiseDomain CDPEnergyWiseSubtype = 0x00000008
|
||||
CDPEnergyWiseName CDPEnergyWiseSubtype = 0x00000009
|
||||
CDPEnergyWiseReplyTo CDPEnergyWiseSubtype = 0x00000017
|
||||
)
|
||||
|
||||
// CDPEnergyWise is used by CDP to monitor and control power usage.
|
||||
type CDPEnergyWise struct {
|
||||
EncryptedData []byte
|
||||
Unknown1 uint32
|
||||
SequenceNumber uint32
|
||||
ModelNumber string
|
||||
Unknown2 uint16
|
||||
HardwareID string
|
||||
SerialNum string
|
||||
Unknown3 []byte
|
||||
Role string
|
||||
Domain string
|
||||
Name string
|
||||
ReplyUnknown1 []byte
|
||||
ReplyPort []byte
|
||||
ReplyAddress []byte
|
||||
ReplyUnknown2 []byte
|
||||
ReplyUnknown3 []byte
|
||||
}
|
||||
|
||||
// CiscoDiscoveryInfo represents the decoded details for a set of CiscoDiscoveryValues
|
||||
type CiscoDiscoveryInfo struct {
|
||||
BaseLayer
|
||||
CDPHello
|
||||
DeviceID string
|
||||
Addresses []net.IP
|
||||
PortID string
|
||||
Capabilities CDPCapabilities
|
||||
Version string
|
||||
Platform string
|
||||
IPPrefixes []net.IPNet
|
||||
VTPDomain string
|
||||
NativeVLAN uint16
|
||||
FullDuplex bool
|
||||
VLANReply CDPVLANDialogue
|
||||
VLANQuery CDPVLANDialogue
|
||||
PowerConsumption uint16
|
||||
MTU uint32
|
||||
ExtendedTrust uint8
|
||||
UntrustedCOS uint8
|
||||
SysName string
|
||||
SysOID string
|
||||
MgmtAddresses []net.IP
|
||||
Location CDPLocation
|
||||
PowerRequest CDPPowerDialogue
|
||||
PowerAvailable CDPPowerDialogue
|
||||
SparePairPoe CDPSparePairPoE
|
||||
EnergyWise CDPEnergyWise
|
||||
Unknown []CiscoDiscoveryValue
|
||||
}
|
||||
|
||||
// LayerType returns gopacket.LayerTypeCiscoDiscovery.
|
||||
func (c *CiscoDiscovery) LayerType() gopacket.LayerType {
|
||||
return LayerTypeCiscoDiscovery
|
||||
}
|
||||
|
||||
func decodeCiscoDiscovery(data []byte, p gopacket.PacketBuilder) error {
|
||||
c := &CiscoDiscovery{
|
||||
Version: data[0],
|
||||
TTL: data[1],
|
||||
Checksum: binary.BigEndian.Uint16(data[2:4]),
|
||||
}
|
||||
if c.Version != 1 && c.Version != 2 {
|
||||
return fmt.Errorf("Invalid CiscoDiscovery version number %d", c.Version)
|
||||
}
|
||||
var err error
|
||||
c.Values, err = decodeCiscoDiscoveryTLVs(data[4:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.Contents = data[0:4]
|
||||
c.Payload = data[4:]
|
||||
p.AddLayer(c)
|
||||
return p.NextDecoder(gopacket.DecodeFunc(decodeCiscoDiscoveryInfo))
|
||||
}
|
||||
|
||||
// LayerType returns gopacket.LayerTypeCiscoDiscoveryInfo.
|
||||
func (c *CiscoDiscoveryInfo) LayerType() gopacket.LayerType {
|
||||
return LayerTypeCiscoDiscoveryInfo
|
||||
}
|
||||
|
||||
func decodeCiscoDiscoveryTLVs(data []byte) (values []CiscoDiscoveryValue, err error) {
|
||||
for len(data) > 0 {
|
||||
val := CiscoDiscoveryValue{
|
||||
Type: CDPTLVType(binary.BigEndian.Uint16(data[:2])),
|
||||
Length: binary.BigEndian.Uint16(data[2:4]),
|
||||
}
|
||||
if val.Length < 4 {
|
||||
err = fmt.Errorf("Invalid CiscoDiscovery value length %d", val.Length)
|
||||
break
|
||||
}
|
||||
val.Value = data[4:val.Length]
|
||||
values = append(values, val)
|
||||
data = data[val.Length:]
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func decodeCiscoDiscoveryInfo(data []byte, p gopacket.PacketBuilder) error {
|
||||
var err error
|
||||
info := &CiscoDiscoveryInfo{BaseLayer: BaseLayer{Contents: data}}
|
||||
p.AddLayer(info)
|
||||
values, err := decodeCiscoDiscoveryTLVs(data)
|
||||
if err != nil { // Unlikely, as parent decode will fail, but better safe...
|
||||
return err
|
||||
}
|
||||
for _, val := range values {
|
||||
switch val.Type {
|
||||
case CDPTLVDevID:
|
||||
info.DeviceID = string(val.Value)
|
||||
case CDPTLVAddress:
|
||||
if err = checkCDPTLVLen(val, 4); err != nil {
|
||||
return err
|
||||
}
|
||||
info.Addresses, err = decodeAddresses(val.Value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
case CDPTLVPortID:
|
||||
info.PortID = string(val.Value)
|
||||
case CDPTLVCapabilities:
|
||||
if err = checkCDPTLVLen(val, 4); err != nil {
|
||||
return err
|
||||
}
|
||||
val := CDPCapability(binary.BigEndian.Uint32(val.Value[0:4]))
|
||||
info.Capabilities.L3Router = (val&CDPCapMaskRouter > 0)
|
||||
info.Capabilities.TBBridge = (val&CDPCapMaskTBBridge > 0)
|
||||
info.Capabilities.SPBridge = (val&CDPCapMaskSPBridge > 0)
|
||||
info.Capabilities.L2Switch = (val&CDPCapMaskSwitch > 0)
|
||||
info.Capabilities.IsHost = (val&CDPCapMaskHost > 0)
|
||||
info.Capabilities.IGMPFilter = (val&CDPCapMaskIGMPFilter > 0)
|
||||
info.Capabilities.L1Repeater = (val&CDPCapMaskRepeater > 0)
|
||||
info.Capabilities.IsPhone = (val&CDPCapMaskPhone > 0)
|
||||
info.Capabilities.RemotelyManaged = (val&CDPCapMaskRemote > 0)
|
||||
case CDPTLVVersion:
|
||||
info.Version = string(val.Value)
|
||||
case CDPTLVPlatform:
|
||||
info.Platform = string(val.Value)
|
||||
case CDPTLVIPPrefix:
|
||||
v := val.Value
|
||||
l := len(v)
|
||||
if l%5 == 0 && l >= 5 {
|
||||
for len(v) > 0 {
|
||||
_, ipnet, _ := net.ParseCIDR(fmt.Sprintf("%d.%d.%d.%d/%d", v[0], v[1], v[2], v[3], v[4]))
|
||||
info.IPPrefixes = append(info.IPPrefixes, *ipnet)
|
||||
v = v[5:]
|
||||
}
|
||||
} else {
|
||||
return fmt.Errorf("Invalid TLV %v length %d", val.Type, len(val.Value))
|
||||
}
|
||||
case CDPTLVHello:
|
||||
if err = checkCDPTLVLen(val, 32); err != nil {
|
||||
return err
|
||||
}
|
||||
v := val.Value
|
||||
info.CDPHello.OUI = v[0:3]
|
||||
info.CDPHello.ProtocolID = binary.BigEndian.Uint16(v[3:5])
|
||||
info.CDPHello.ClusterMaster = v[5:9]
|
||||
info.CDPHello.Unknown1 = v[9:13]
|
||||
info.CDPHello.Version = v[13]
|
||||
info.CDPHello.SubVersion = v[14]
|
||||
info.CDPHello.Status = v[15]
|
||||
info.CDPHello.Unknown2 = v[16]
|
||||
info.CDPHello.ClusterCommander = v[17:23]
|
||||
info.CDPHello.SwitchMAC = v[23:29]
|
||||
info.CDPHello.Unknown3 = v[29]
|
||||
info.CDPHello.ManagementVLAN = binary.BigEndian.Uint16(v[30:32])
|
||||
case CDPTLVVTPDomain:
|
||||
info.VTPDomain = string(val.Value)
|
||||
case CDPTLVNativeVLAN:
|
||||
if err = checkCDPTLVLen(val, 2); err != nil {
|
||||
return err
|
||||
}
|
||||
info.NativeVLAN = binary.BigEndian.Uint16(val.Value[0:2])
|
||||
case CDPTLVFullDuplex:
|
||||
if err = checkCDPTLVLen(val, 1); err != nil {
|
||||
return err
|
||||
}
|
||||
info.FullDuplex = (val.Value[0] == 1)
|
||||
case CDPTLVVLANReply:
|
||||
if err = checkCDPTLVLen(val, 3); err != nil {
|
||||
return err
|
||||
}
|
||||
info.VLANReply.ID = uint8(val.Value[0])
|
||||
info.VLANReply.VLAN = binary.BigEndian.Uint16(val.Value[1:3])
|
||||
case CDPTLVVLANQuery:
|
||||
if err = checkCDPTLVLen(val, 3); err != nil {
|
||||
return err
|
||||
}
|
||||
info.VLANQuery.ID = uint8(val.Value[0])
|
||||
info.VLANQuery.VLAN = binary.BigEndian.Uint16(val.Value[1:3])
|
||||
case CDPTLVPower:
|
||||
if err = checkCDPTLVLen(val, 2); err != nil {
|
||||
return err
|
||||
}
|
||||
info.PowerConsumption = binary.BigEndian.Uint16(val.Value[0:2])
|
||||
case CDPTLVMTU:
|
||||
if err = checkCDPTLVLen(val, 4); err != nil {
|
||||
return err
|
||||
}
|
||||
info.MTU = binary.BigEndian.Uint32(val.Value[0:4])
|
||||
case CDPTLVExtendedTrust:
|
||||
if err = checkCDPTLVLen(val, 1); err != nil {
|
||||
return err
|
||||
}
|
||||
info.ExtendedTrust = uint8(val.Value[0])
|
||||
case CDPTLVUntrustedCOS:
|
||||
if err = checkCDPTLVLen(val, 1); err != nil {
|
||||
return err
|
||||
}
|
||||
info.UntrustedCOS = uint8(val.Value[0])
|
||||
case CDPTLVSysName:
|
||||
info.SysName = string(val.Value)
|
||||
case CDPTLVSysOID:
|
||||
info.SysOID = string(val.Value)
|
||||
case CDPTLVMgmtAddresses:
|
||||
if err = checkCDPTLVLen(val, 4); err != nil {
|
||||
return err
|
||||
}
|
||||
info.MgmtAddresses, err = decodeAddresses(val.Value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
case CDPTLVLocation:
|
||||
if err = checkCDPTLVLen(val, 2); err != nil {
|
||||
return err
|
||||
}
|
||||
info.Location.Type = uint8(val.Value[0])
|
||||
info.Location.Location = string(val.Value[1:])
|
||||
|
||||
// case CDPTLVLExternalPortID:
|
||||
// Undocumented
|
||||
case CDPTLVPowerRequested:
|
||||
if err = checkCDPTLVLen(val, 4); err != nil {
|
||||
return err
|
||||
}
|
||||
info.PowerRequest.ID = binary.BigEndian.Uint16(val.Value[0:2])
|
||||
info.PowerRequest.MgmtID = binary.BigEndian.Uint16(val.Value[2:4])
|
||||
for n := 4; n < len(val.Value); n += 4 {
|
||||
info.PowerRequest.Values = append(info.PowerRequest.Values, binary.BigEndian.Uint32(val.Value[n:n+4]))
|
||||
}
|
||||
case CDPTLVPowerAvailable:
|
||||
if err = checkCDPTLVLen(val, 4); err != nil {
|
||||
return err
|
||||
}
|
||||
info.PowerAvailable.ID = binary.BigEndian.Uint16(val.Value[0:2])
|
||||
info.PowerAvailable.MgmtID = binary.BigEndian.Uint16(val.Value[2:4])
|
||||
for n := 4; n < len(val.Value); n += 4 {
|
||||
info.PowerAvailable.Values = append(info.PowerAvailable.Values, binary.BigEndian.Uint32(val.Value[n:n+4]))
|
||||
}
|
||||
// case CDPTLVPortUnidirectional
|
||||
// Undocumented
|
||||
case CDPTLVEnergyWise:
|
||||
if err = checkCDPTLVLen(val, 72); err != nil {
|
||||
return err
|
||||
}
|
||||
info.EnergyWise.EncryptedData = val.Value[0:20]
|
||||
info.EnergyWise.Unknown1 = binary.BigEndian.Uint32(val.Value[20:24])
|
||||
info.EnergyWise.SequenceNumber = binary.BigEndian.Uint32(val.Value[24:28])
|
||||
info.EnergyWise.ModelNumber = string(val.Value[28:44])
|
||||
info.EnergyWise.Unknown2 = binary.BigEndian.Uint16(val.Value[44:46])
|
||||
info.EnergyWise.HardwareID = string(val.Value[46:49])
|
||||
info.EnergyWise.SerialNum = string(val.Value[49:60])
|
||||
info.EnergyWise.Unknown3 = val.Value[60:68]
|
||||
tlvLen := binary.BigEndian.Uint16(val.Value[68:70])
|
||||
tlvNum := binary.BigEndian.Uint16(val.Value[70:72])
|
||||
data := val.Value[72:]
|
||||
if len(data) < int(tlvLen) {
|
||||
return fmt.Errorf("Invalid TLV length %d vs %d", tlvLen, len(data))
|
||||
}
|
||||
numSeen := 0
|
||||
for len(data) > 8 {
|
||||
numSeen++
|
||||
if numSeen > int(tlvNum) { // Too many TLV's ?
|
||||
return fmt.Errorf("Too many TLV's - wanted %d, saw %d", tlvNum, numSeen)
|
||||
}
|
||||
tType := CDPEnergyWiseSubtype(binary.BigEndian.Uint32(data[0:4]))
|
||||
tLen := int(binary.BigEndian.Uint32(data[4:8]))
|
||||
if tLen > len(data)-8 {
|
||||
return fmt.Errorf("Invalid TLV length %d vs %d", tLen, len(data)-8)
|
||||
}
|
||||
data = data[8:]
|
||||
switch tType {
|
||||
case CDPEnergyWiseRole:
|
||||
info.EnergyWise.Role = string(data[:])
|
||||
case CDPEnergyWiseDomain:
|
||||
info.EnergyWise.Domain = string(data[:])
|
||||
case CDPEnergyWiseName:
|
||||
info.EnergyWise.Name = string(data[:])
|
||||
case CDPEnergyWiseReplyTo:
|
||||
if len(data) >= 18 {
|
||||
info.EnergyWise.ReplyUnknown1 = data[0:2]
|
||||
info.EnergyWise.ReplyPort = data[2:4]
|
||||
info.EnergyWise.ReplyAddress = data[4:8]
|
||||
info.EnergyWise.ReplyUnknown2 = data[8:10]
|
||||
info.EnergyWise.ReplyUnknown3 = data[10:14]
|
||||
}
|
||||
}
|
||||
data = data[tLen:]
|
||||
}
|
||||
case CDPTLVSparePairPOE:
|
||||
if err = checkCDPTLVLen(val, 1); err != nil {
|
||||
return err
|
||||
}
|
||||
v := val.Value[0]
|
||||
info.SparePairPoe.PSEFourWire = (v&CDPPoEFourWire > 0)
|
||||
info.SparePairPoe.PDArchShared = (v&CDPPoEPDArch > 0)
|
||||
info.SparePairPoe.PDRequestOn = (v&CDPPoEPDRequest > 0)
|
||||
info.SparePairPoe.PSEOn = (v&CDPPoEPSE > 0)
|
||||
default:
|
||||
info.Unknown = append(info.Unknown, val)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// CDP Protocol Types
|
||||
const (
|
||||
CDPProtocolTypeNLPID byte = 1
|
||||
CDPProtocolType802_2 byte = 2
|
||||
)
|
||||
|
||||
// CDPAddressType is used to define TLV values within CDP addresses.
|
||||
type CDPAddressType uint64
|
||||
|
||||
// CDP Address types.
|
||||
const (
|
||||
CDPAddressTypeCLNP CDPAddressType = 0x81
|
||||
CDPAddressTypeIPV4 CDPAddressType = 0xcc
|
||||
CDPAddressTypeIPV6 CDPAddressType = 0xaaaa030000000800
|
||||
CDPAddressTypeDECNET CDPAddressType = 0xaaaa030000006003
|
||||
CDPAddressTypeAPPLETALK CDPAddressType = 0xaaaa03000000809b
|
||||
CDPAddressTypeIPX CDPAddressType = 0xaaaa030000008137
|
||||
CDPAddressTypeVINES CDPAddressType = 0xaaaa0300000080c4
|
||||
CDPAddressTypeXNS CDPAddressType = 0xaaaa030000000600
|
||||
CDPAddressTypeAPOLLO CDPAddressType = 0xaaaa030000008019
|
||||
)
|
||||
|
||||
func decodeAddresses(v []byte) (addresses []net.IP, err error) {
|
||||
numaddr := int(binary.BigEndian.Uint32(v[0:4]))
|
||||
if numaddr < 1 {
|
||||
return nil, fmt.Errorf("Invalid Address TLV number %d", numaddr)
|
||||
}
|
||||
v = v[4:]
|
||||
if len(v) < numaddr*8 {
|
||||
return nil, fmt.Errorf("Invalid Address TLV length %d", len(v))
|
||||
}
|
||||
for i := 0; i < numaddr; i++ {
|
||||
prottype := v[0]
|
||||
if prottype != CDPProtocolTypeNLPID && prottype != CDPProtocolType802_2 { // invalid protocol type
|
||||
return nil, fmt.Errorf("Invalid Address Protocol %d", prottype)
|
||||
}
|
||||
protlen := int(v[1])
|
||||
if (prottype == CDPProtocolTypeNLPID && protlen != 1) ||
|
||||
(prottype == CDPProtocolType802_2 && protlen != 3 && protlen != 8) { // invalid length
|
||||
return nil, fmt.Errorf("Invalid Address Protocol length %d", protlen)
|
||||
}
|
||||
plen := make([]byte, 8)
|
||||
copy(plen[8-protlen:], v[2:2+protlen])
|
||||
protocol := CDPAddressType(binary.BigEndian.Uint64(plen))
|
||||
v = v[2+protlen:]
|
||||
addrlen := binary.BigEndian.Uint16(v[0:2])
|
||||
ab := v[2 : 2+addrlen]
|
||||
if protocol == CDPAddressTypeIPV4 && addrlen == 4 {
|
||||
addresses = append(addresses, net.IPv4(ab[0], ab[1], ab[2], ab[3]))
|
||||
} else if protocol == CDPAddressTypeIPV6 && addrlen == 16 {
|
||||
addresses = append(addresses, net.IP(ab))
|
||||
} else {
|
||||
// only handle IPV4 & IPV6 for now
|
||||
}
|
||||
v = v[2+addrlen:]
|
||||
if len(v) < 8 {
|
||||
break
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (t CDPTLVType) String() (s string) {
|
||||
switch t {
|
||||
case CDPTLVDevID:
|
||||
s = "Device ID"
|
||||
case CDPTLVAddress:
|
||||
s = "Addresses"
|
||||
case CDPTLVPortID:
|
||||
s = "Port ID"
|
||||
case CDPTLVCapabilities:
|
||||
s = "Capabilities"
|
||||
case CDPTLVVersion:
|
||||
s = "Software Version"
|
||||
case CDPTLVPlatform:
|
||||
s = "Platform"
|
||||
case CDPTLVIPPrefix:
|
||||
s = "IP Prefix"
|
||||
case CDPTLVHello:
|
||||
s = "Protocol Hello"
|
||||
case CDPTLVVTPDomain:
|
||||
s = "VTP Management Domain"
|
||||
case CDPTLVNativeVLAN:
|
||||
s = "Native VLAN"
|
||||
case CDPTLVFullDuplex:
|
||||
s = "Full Duplex"
|
||||
case CDPTLVVLANReply:
|
||||
s = "VoIP VLAN Reply"
|
||||
case CDPTLVVLANQuery:
|
||||
s = "VLANQuery"
|
||||
case CDPTLVPower:
|
||||
s = "Power consumption"
|
||||
case CDPTLVMTU:
|
||||
s = "MTU"
|
||||
case CDPTLVExtendedTrust:
|
||||
s = "Extended Trust Bitmap"
|
||||
case CDPTLVUntrustedCOS:
|
||||
s = "Untrusted Port CoS"
|
||||
case CDPTLVSysName:
|
||||
s = "System Name"
|
||||
case CDPTLVSysOID:
|
||||
s = "System OID"
|
||||
case CDPTLVMgmtAddresses:
|
||||
s = "Management Addresses"
|
||||
case CDPTLVLocation:
|
||||
s = "Location"
|
||||
case CDPTLVExternalPortID:
|
||||
s = "External Port ID"
|
||||
case CDPTLVPowerRequested:
|
||||
s = "Power Requested"
|
||||
case CDPTLVPowerAvailable:
|
||||
s = "Power Available"
|
||||
case CDPTLVPortUnidirectional:
|
||||
s = "Port Unidirectional"
|
||||
case CDPTLVEnergyWise:
|
||||
s = "Energy Wise"
|
||||
case CDPTLVSparePairPOE:
|
||||
s = "Spare Pair POE"
|
||||
default:
|
||||
s = "Unknown"
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (a CDPAddressType) String() (s string) {
|
||||
switch a {
|
||||
case CDPAddressTypeCLNP:
|
||||
s = "Connectionless Network Protocol"
|
||||
case CDPAddressTypeIPV4:
|
||||
s = "IPv4"
|
||||
case CDPAddressTypeIPV6:
|
||||
s = "IPv6"
|
||||
case CDPAddressTypeDECNET:
|
||||
s = "DECnet Phase IV"
|
||||
case CDPAddressTypeAPPLETALK:
|
||||
s = "Apple Talk"
|
||||
case CDPAddressTypeIPX:
|
||||
s = "Novell IPX"
|
||||
case CDPAddressTypeVINES:
|
||||
s = "Banyan VINES"
|
||||
case CDPAddressTypeXNS:
|
||||
s = "Xerox Network Systems"
|
||||
case CDPAddressTypeAPOLLO:
|
||||
s = "Apollo"
|
||||
default:
|
||||
s = "Unknown"
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (t CDPEnergyWiseSubtype) String() (s string) {
|
||||
switch t {
|
||||
case CDPEnergyWiseRole:
|
||||
s = "Role"
|
||||
case CDPEnergyWiseDomain:
|
||||
s = "Domain"
|
||||
case CDPEnergyWiseName:
|
||||
s = "Name"
|
||||
case CDPEnergyWiseReplyTo:
|
||||
s = "ReplyTo"
|
||||
default:
|
||||
s = "Unknown"
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func checkCDPTLVLen(v CiscoDiscoveryValue, l int) (err error) {
|
||||
if len(v.Value) < l {
|
||||
err = fmt.Errorf("Invalid TLV %v length %d", v.Type, len(v.Value))
|
||||
}
|
||||
return
|
||||
}
|
@ -0,0 +1,109 @@
|
||||
// Copyright 2012 Google, Inc. All rights reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the LICENSE file in the root of the source
|
||||
// tree.
|
||||
|
||||
package layers
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"github.com/google/gopacket"
|
||||
)
|
||||
|
||||
// EthernetCTPFunction is the function code used by the EthernetCTP protocol to identify each
|
||||
// EthernetCTP layer.
|
||||
type EthernetCTPFunction uint16
|
||||
|
||||
// EthernetCTPFunction values.
|
||||
const (
|
||||
EthernetCTPFunctionReply EthernetCTPFunction = 1
|
||||
EthernetCTPFunctionForwardData EthernetCTPFunction = 2
|
||||
)
|
||||
|
||||
// EthernetCTP implements the EthernetCTP protocol, see http://www.mit.edu/people/jhawk/ctp.html.
|
||||
// We split EthernetCTP up into the top-level EthernetCTP layer, followed by zero or more
|
||||
// EthernetCTPForwardData layers, followed by a final EthernetCTPReply layer.
|
||||
type EthernetCTP struct {
|
||||
BaseLayer
|
||||
SkipCount uint16
|
||||
}
|
||||
|
||||
// LayerType returns gopacket.LayerTypeEthernetCTP.
|
||||
func (c *EthernetCTP) LayerType() gopacket.LayerType {
|
||||
return LayerTypeEthernetCTP
|
||||
}
|
||||
|
||||
// EthernetCTPForwardData is the ForwardData layer inside EthernetCTP. See EthernetCTP's docs for more
|
||||
// details.
|
||||
type EthernetCTPForwardData struct {
|
||||
BaseLayer
|
||||
Function EthernetCTPFunction
|
||||
ForwardAddress []byte
|
||||
}
|
||||
|
||||
// LayerType returns gopacket.LayerTypeEthernetCTPForwardData.
|
||||
func (c *EthernetCTPForwardData) LayerType() gopacket.LayerType {
|
||||
return LayerTypeEthernetCTPForwardData
|
||||
}
|
||||
|
||||
// ForwardEndpoint returns the EthernetCTPForwardData ForwardAddress as an endpoint.
|
||||
func (c *EthernetCTPForwardData) ForwardEndpoint() gopacket.Endpoint {
|
||||
return gopacket.NewEndpoint(EndpointMAC, c.ForwardAddress)
|
||||
}
|
||||
|
||||
// EthernetCTPReply is the Reply layer inside EthernetCTP. See EthernetCTP's docs for more details.
|
||||
type EthernetCTPReply struct {
|
||||
BaseLayer
|
||||
Function EthernetCTPFunction
|
||||
ReceiptNumber uint16
|
||||
Data []byte
|
||||
}
|
||||
|
||||
// LayerType returns gopacket.LayerTypeEthernetCTPReply.
|
||||
func (c *EthernetCTPReply) LayerType() gopacket.LayerType {
|
||||
return LayerTypeEthernetCTPReply
|
||||
}
|
||||
|
||||
// Payload returns the EthernetCTP reply's Data bytes.
|
||||
func (c *EthernetCTPReply) Payload() []byte { return c.Data }
|
||||
|
||||
func decodeEthernetCTP(data []byte, p gopacket.PacketBuilder) error {
|
||||
c := &EthernetCTP{
|
||||
SkipCount: binary.LittleEndian.Uint16(data[:2]),
|
||||
BaseLayer: BaseLayer{data[:2], data[2:]},
|
||||
}
|
||||
if c.SkipCount%2 != 0 {
|
||||
return fmt.Errorf("EthernetCTP skip count is odd: %d", c.SkipCount)
|
||||
}
|
||||
p.AddLayer(c)
|
||||
return p.NextDecoder(gopacket.DecodeFunc(decodeEthernetCTPFromFunctionType))
|
||||
}
|
||||
|
||||
// decodeEthernetCTPFromFunctionType reads in the first 2 bytes to determine the EthernetCTP
|
||||
// layer type to decode next, then decodes based on that.
|
||||
func decodeEthernetCTPFromFunctionType(data []byte, p gopacket.PacketBuilder) error {
|
||||
function := EthernetCTPFunction(binary.LittleEndian.Uint16(data[:2]))
|
||||
switch function {
|
||||
case EthernetCTPFunctionReply:
|
||||
reply := &EthernetCTPReply{
|
||||
Function: function,
|
||||
ReceiptNumber: binary.LittleEndian.Uint16(data[2:4]),
|
||||
Data: data[4:],
|
||||
BaseLayer: BaseLayer{data, nil},
|
||||
}
|
||||
p.AddLayer(reply)
|
||||
p.SetApplicationLayer(reply)
|
||||
return nil
|
||||
case EthernetCTPFunctionForwardData:
|
||||
forward := &EthernetCTPForwardData{
|
||||
Function: function,
|
||||
ForwardAddress: data[2:8],
|
||||
BaseLayer: BaseLayer{data[:8], data[8:]},
|
||||
}
|
||||
p.AddLayer(forward)
|
||||
return p.NextDecoder(gopacket.DecodeFunc(decodeEthernetCTPFromFunctionType))
|
||||
}
|
||||
return fmt.Errorf("Unknown EthernetCTP function type %v", function)
|
||||
}
|
@ -0,0 +1,571 @@
|
||||
// Copyright 2016 Google, Inc. All rights reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the LICENSE file in the root of the source
|
||||
// tree.
|
||||
|
||||
package layers
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
|
||||
"github.com/google/gopacket"
|
||||
)
|
||||
|
||||
// DHCPOp rerprents a bootp operation
|
||||
type DHCPOp byte
|
||||
|
||||
// bootp operations
|
||||
const (
|
||||
DHCPOpRequest DHCPOp = 1
|
||||
DHCPOpReply DHCPOp = 2
|
||||
)
|
||||
|
||||
// String returns a string version of a DHCPOp.
|
||||
func (o DHCPOp) String() string {
|
||||
switch o {
|
||||
case DHCPOpRequest:
|
||||
return "Request"
|
||||
case DHCPOpReply:
|
||||
return "Reply"
|
||||
default:
|
||||
return "Unknown"
|
||||
}
|
||||
}
|
||||
|
||||
// DHCPMsgType represents a DHCP operation
|
||||
type DHCPMsgType byte
|
||||
|
||||
// Constants that represent DHCP operations
|
||||
const (
|
||||
DHCPMsgTypeUnspecified DHCPMsgType = iota
|
||||
DHCPMsgTypeDiscover
|
||||
DHCPMsgTypeOffer
|
||||
DHCPMsgTypeRequest
|
||||
DHCPMsgTypeDecline
|
||||
DHCPMsgTypeAck
|
||||
DHCPMsgTypeNak
|
||||
DHCPMsgTypeRelease
|
||||
DHCPMsgTypeInform
|
||||
)
|
||||
|
||||
// String returns a string version of a DHCPMsgType.
|
||||
func (o DHCPMsgType) String() string {
|
||||
switch o {
|
||||
case DHCPMsgTypeUnspecified:
|
||||
return "Unspecified"
|
||||
case DHCPMsgTypeDiscover:
|
||||
return "Discover"
|
||||
case DHCPMsgTypeOffer:
|
||||
return "Offer"
|
||||
case DHCPMsgTypeRequest:
|
||||
return "Request"
|
||||
case DHCPMsgTypeDecline:
|
||||
return "Decline"
|
||||
case DHCPMsgTypeAck:
|
||||
return "Ack"
|
||||
case DHCPMsgTypeNak:
|
||||
return "Nak"
|
||||
case DHCPMsgTypeRelease:
|
||||
return "Release"
|
||||
case DHCPMsgTypeInform:
|
||||
return "Inform"
|
||||
default:
|
||||
return "Unknown"
|
||||
}
|
||||
}
|
||||
|
||||
//DHCPMagic is the RFC 2131 "magic cooke" for DHCP.
|
||||
var DHCPMagic uint32 = 0x63825363
|
||||
|
||||
// DHCPv4 contains data for a single DHCP packet.
|
||||
type DHCPv4 struct {
|
||||
BaseLayer
|
||||
Operation DHCPOp
|
||||
HardwareType LinkType
|
||||
HardwareLen uint8
|
||||
HardwareOpts uint8
|
||||
Xid uint32
|
||||
Secs uint16
|
||||
Flags uint16
|
||||
ClientIP net.IP
|
||||
YourClientIP net.IP
|
||||
NextServerIP net.IP
|
||||
RelayAgentIP net.IP
|
||||
ClientHWAddr net.HardwareAddr
|
||||
ServerName []byte
|
||||
File []byte
|
||||
Options DHCPOptions
|
||||
}
|
||||
|
||||
// DHCPOptions is used to get nicely printed option lists which would normally
|
||||
// be cut off after 5 options.
|
||||
type DHCPOptions []DHCPOption
|
||||
|
||||
// String returns a string version of the options list.
|
||||
func (o DHCPOptions) 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()
|
||||
}
|
||||
|
||||
// LayerType returns gopacket.LayerTypeDHCPv4
|
||||
func (d *DHCPv4) LayerType() gopacket.LayerType { return LayerTypeDHCPv4 }
|
||||
|
||||
// DecodeFromBytes decodes the given bytes into this layer.
|
||||
func (d *DHCPv4) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
|
||||
d.Operation = DHCPOp(data[0])
|
||||
d.HardwareType = LinkType(data[1])
|
||||
d.HardwareLen = data[2]
|
||||
d.HardwareOpts = data[3]
|
||||
d.Xid = binary.BigEndian.Uint32(data[4:8])
|
||||
d.Secs = binary.BigEndian.Uint16(data[8:10])
|
||||
d.Flags = binary.BigEndian.Uint16(data[10:12])
|
||||
d.ClientIP = net.IP(data[12:16])
|
||||
d.YourClientIP = net.IP(data[16:20])
|
||||
d.NextServerIP = net.IP(data[20:24])
|
||||
d.RelayAgentIP = net.IP(data[24:28])
|
||||
d.ClientHWAddr = net.HardwareAddr(data[28 : 28+d.HardwareLen])
|
||||
d.ServerName = data[44:108]
|
||||
d.File = data[108:236]
|
||||
if binary.BigEndian.Uint32(data[236:240]) != DHCPMagic {
|
||||
return errors.New("Bad DHCP header")
|
||||
}
|
||||
|
||||
if len(data) <= 240 {
|
||||
// DHCP Packet could have no option (??)
|
||||
return nil
|
||||
}
|
||||
|
||||
options := data[240:]
|
||||
|
||||
stop := len(options)
|
||||
start := 0
|
||||
for start < stop {
|
||||
o := DHCPOption{}
|
||||
if err := o.decode(options[start:]); err != nil {
|
||||
return err
|
||||
}
|
||||
if o.Type == DHCPOptEnd {
|
||||
break
|
||||
}
|
||||
d.Options = append(d.Options, o)
|
||||
// Check if the option is a single byte pad
|
||||
if o.Type == DHCPOptPad {
|
||||
start++
|
||||
} else {
|
||||
start += int(o.Length) + 2
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Len returns the length of a DHCPv4 packet.
|
||||
func (d *DHCPv4) Len() uint16 {
|
||||
n := uint16(240)
|
||||
for _, o := range d.Options {
|
||||
if o.Type == DHCPOptPad {
|
||||
n++
|
||||
} else {
|
||||
n += uint16(o.Length) + 2
|
||||
}
|
||||
}
|
||||
n++ // for opt end
|
||||
return n
|
||||
}
|
||||
|
||||
// SerializeTo writes the serialized form of this layer into the
|
||||
// SerializationBuffer, implementing gopacket.SerializableLayer.
|
||||
// See the docs for gopacket.SerializableLayer for more info.
|
||||
func (d *DHCPv4) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
|
||||
plen := int(d.Len())
|
||||
|
||||
data, err := b.PrependBytes(plen)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
data[0] = byte(d.Operation)
|
||||
data[1] = byte(d.HardwareType)
|
||||
if opts.FixLengths {
|
||||
d.HardwareLen = uint8(len(d.ClientHWAddr))
|
||||
}
|
||||
data[2] = d.HardwareLen
|
||||
data[3] = d.HardwareOpts
|
||||
binary.BigEndian.PutUint32(data[4:8], d.Xid)
|
||||
binary.BigEndian.PutUint16(data[8:10], d.Secs)
|
||||
binary.BigEndian.PutUint16(data[10:12], d.Flags)
|
||||
copy(data[12:16], d.ClientIP.To4())
|
||||
copy(data[16:20], d.YourClientIP.To4())
|
||||
copy(data[20:24], d.NextServerIP.To4())
|
||||
copy(data[24:28], d.RelayAgentIP.To4())
|
||||
copy(data[28:44], d.ClientHWAddr)
|
||||
copy(data[44:108], d.ServerName)
|
||||
copy(data[108:236], d.File)
|
||||
binary.BigEndian.PutUint32(data[236:240], DHCPMagic)
|
||||
|
||||
if len(d.Options) > 0 {
|
||||
offset := 240
|
||||
for _, o := range d.Options {
|
||||
if err := o.encode(data[offset:]); err != nil {
|
||||
return err
|
||||
}
|
||||
// A pad option is only a single byte
|
||||
if o.Type == DHCPOptPad {
|
||||
offset++
|
||||
} else {
|
||||
offset += 2 + len(o.Data)
|
||||
}
|
||||
}
|
||||
optend := NewDHCPOption(DHCPOptEnd, nil)
|
||||
if err := optend.encode(data[offset:]); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// CanDecode returns the set of layer types that this DecodingLayer can decode.
|
||||
func (d *DHCPv4) CanDecode() gopacket.LayerClass {
|
||||
return LayerTypeDHCPv4
|
||||
}
|
||||
|
||||
// NextLayerType returns the layer type contained by this DecodingLayer.
|
||||
func (d *DHCPv4) NextLayerType() gopacket.LayerType {
|
||||
return gopacket.LayerTypePayload
|
||||
}
|
||||
|
||||
func decodeDHCPv4(data []byte, p gopacket.PacketBuilder) error {
|
||||
dhcp := &DHCPv4{}
|
||||
err := dhcp.DecodeFromBytes(data, p)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
p.AddLayer(dhcp)
|
||||
return p.NextDecoder(gopacket.LayerTypePayload)
|
||||
}
|
||||
|
||||
// DHCPOpt represents a DHCP option or parameter from RFC-2132
|
||||
type DHCPOpt byte
|
||||
|
||||
// Constants for the DHCPOpt options.
|
||||
const (
|
||||
DHCPOptPad DHCPOpt = 0
|
||||
DHCPOptSubnetMask DHCPOpt = 1 // 4, net.IP
|
||||
DHCPOptTimeOffset DHCPOpt = 2 // 4, int32 (signed seconds from UTC)
|
||||
DHCPOptRouter DHCPOpt = 3 // n*4, [n]net.IP
|
||||
DHCPOptTimeServer DHCPOpt = 4 // n*4, [n]net.IP
|
||||
DHCPOptNameServer DHCPOpt = 5 // n*4, [n]net.IP
|
||||
DHCPOptDNS DHCPOpt = 6 // n*4, [n]net.IP
|
||||
DHCPOptLogServer DHCPOpt = 7 // n*4, [n]net.IP
|
||||
DHCPOptCookieServer DHCPOpt = 8 // n*4, [n]net.IP
|
||||
DHCPOptLPRServer DHCPOpt = 9 // n*4, [n]net.IP
|
||||
DHCPOptImpressServer DHCPOpt = 10 // n*4, [n]net.IP
|
||||
DHCPOptResLocServer DHCPOpt = 11 // n*4, [n]net.IP
|
||||
DHCPOptHostname DHCPOpt = 12 // n, string
|
||||
DHCPOptBootfileSize DHCPOpt = 13 // 2, uint16
|
||||
DHCPOptMeritDumpFile DHCPOpt = 14 // >1, string
|
||||
DHCPOptDomainName DHCPOpt = 15 // n, string
|
||||
DHCPOptSwapServer DHCPOpt = 16 // n*4, [n]net.IP
|
||||
DHCPOptRootPath DHCPOpt = 17 // n, string
|
||||
DHCPOptExtensionsPath DHCPOpt = 18 // n, string
|
||||
DHCPOptIPForwarding DHCPOpt = 19 // 1, bool
|
||||
DHCPOptSourceRouting DHCPOpt = 20 // 1, bool
|
||||
DHCPOptPolicyFilter DHCPOpt = 21 // 8*n, [n]{net.IP/net.IP}
|
||||
DHCPOptDatagramMTU DHCPOpt = 22 // 2, uint16
|
||||
DHCPOptDefaultTTL DHCPOpt = 23 // 1, byte
|
||||
DHCPOptPathMTUAgingTimeout DHCPOpt = 24 // 4, uint32
|
||||
DHCPOptPathPlateuTableOption DHCPOpt = 25 // 2*n, []uint16
|
||||
DHCPOptInterfaceMTU DHCPOpt = 26 // 2, uint16
|
||||
DHCPOptAllSubsLocal DHCPOpt = 27 // 1, bool
|
||||
DHCPOptBroadcastAddr DHCPOpt = 28 // 4, net.IP
|
||||
DHCPOptMaskDiscovery DHCPOpt = 29 // 1, bool
|
||||
DHCPOptMaskSupplier DHCPOpt = 30 // 1, bool
|
||||
DHCPOptRouterDiscovery DHCPOpt = 31 // 1, bool
|
||||
DHCPOptSolicitAddr DHCPOpt = 32 // 4, net.IP
|
||||
DHCPOptStaticRoute DHCPOpt = 33 // n*8, [n]{net.IP/net.IP} -- note the 2nd is router not mask
|
||||
DHCPOptARPTrailers DHCPOpt = 34 // 1, bool
|
||||
DHCPOptARPTimeout DHCPOpt = 35 // 4, uint32
|
||||
DHCPOptEthernetEncap DHCPOpt = 36 // 1, bool
|
||||
DHCPOptTCPTTL DHCPOpt = 37 // 1, byte
|
||||
DHCPOptTCPKeepAliveInt DHCPOpt = 38 // 4, uint32
|
||||
DHCPOptTCPKeepAliveGarbage DHCPOpt = 39 // 1, bool
|
||||
DHCPOptNISDomain DHCPOpt = 40 // n, string
|
||||
DHCPOptNISServers DHCPOpt = 41 // 4*n, [n]net.IP
|
||||
DHCPOptNTPServers DHCPOpt = 42 // 4*n, [n]net.IP
|
||||
DHCPOptVendorOption DHCPOpt = 43 // n, [n]byte // may be encapsulated.
|
||||
DHCPOptNetBIOSTCPNS DHCPOpt = 44 // 4*n, [n]net.IP
|
||||
DHCPOptNetBIOSTCPDDS DHCPOpt = 45 // 4*n, [n]net.IP
|
||||
DHCPOptNETBIOSTCPNodeType DHCPOpt = 46 // 1, magic byte
|
||||
DHCPOptNetBIOSTCPScope DHCPOpt = 47 // n, string
|
||||
DHCPOptXFontServer DHCPOpt = 48 // n, string
|
||||
DHCPOptXDisplayManager DHCPOpt = 49 // n, string
|
||||
DHCPOptRequestIP DHCPOpt = 50 // 4, net.IP
|
||||
DHCPOptLeaseTime DHCPOpt = 51 // 4, uint32
|
||||
DHCPOptExtOptions DHCPOpt = 52 // 1, 1/2/3
|
||||
DHCPOptMessageType DHCPOpt = 53 // 1, 1-7
|
||||
DHCPOptServerID DHCPOpt = 54 // 4, net.IP
|
||||
DHCPOptParamsRequest DHCPOpt = 55 // n, []byte
|
||||
DHCPOptMessage DHCPOpt = 56 // n, 3
|
||||
DHCPOptMaxMessageSize DHCPOpt = 57 // 2, uint16
|
||||
DHCPOptT1 DHCPOpt = 58 // 4, uint32
|
||||
DHCPOptT2 DHCPOpt = 59 // 4, uint32
|
||||
DHCPOptClassID DHCPOpt = 60 // n, []byte
|
||||
DHCPOptClientID DHCPOpt = 61 // n >= 2, []byte
|
||||
DHCPOptDomainSearch DHCPOpt = 119 // n, string
|
||||
DHCPOptSIPServers DHCPOpt = 120 // n, url
|
||||
DHCPOptClasslessStaticRoute DHCPOpt = 121 //
|
||||
DHCPOptEnd DHCPOpt = 255
|
||||
)
|
||||
|
||||
// String returns a string version of a DHCPOpt.
|
||||
func (o DHCPOpt) String() string {
|
||||
switch o {
|
||||
case DHCPOptPad:
|
||||
return "(padding)"
|
||||
case DHCPOptSubnetMask:
|
||||
return "SubnetMask"
|
||||
case DHCPOptTimeOffset:
|
||||
return "TimeOffset"
|
||||
case DHCPOptRouter:
|
||||
return "Router"
|
||||
case DHCPOptTimeServer:
|
||||
return "rfc868" // old time server protocol stringified to dissuade confusion w. NTP
|
||||
case DHCPOptNameServer:
|
||||
return "ien116" // obscure nameserver protocol stringified to dissuade confusion w. DNS
|
||||
case DHCPOptDNS:
|
||||
return "DNS"
|
||||
case DHCPOptLogServer:
|
||||
return "mitLCS" // MIT LCS server protocol yada yada w. Syslog
|
||||
case DHCPOptCookieServer:
|
||||
return "CookieServer"
|
||||
case DHCPOptLPRServer:
|
||||
return "LPRServer"
|
||||
case DHCPOptImpressServer:
|
||||
return "ImpressServer"
|
||||
case DHCPOptResLocServer:
|
||||
return "ResourceLocationServer"
|
||||
case DHCPOptHostname:
|
||||
return "Hostname"
|
||||
case DHCPOptBootfileSize:
|
||||
return "BootfileSize"
|
||||
case DHCPOptMeritDumpFile:
|
||||
return "MeritDumpFile"
|
||||
case DHCPOptDomainName:
|
||||
return "DomainName"
|
||||
case DHCPOptSwapServer:
|
||||
return "SwapServer"
|
||||
case DHCPOptRootPath:
|
||||
return "RootPath"
|
||||
case DHCPOptExtensionsPath:
|
||||
return "ExtensionsPath"
|
||||
case DHCPOptIPForwarding:
|
||||
return "IPForwarding"
|
||||
case DHCPOptSourceRouting:
|
||||
return "SourceRouting"
|
||||
case DHCPOptPolicyFilter:
|
||||
return "PolicyFilter"
|
||||
case DHCPOptDatagramMTU:
|
||||
return "DatagramMTU"
|
||||
case DHCPOptDefaultTTL:
|
||||
return "DefaultTTL"
|
||||
case DHCPOptPathMTUAgingTimeout:
|
||||
return "PathMTUAgingTimeout"
|
||||
case DHCPOptPathPlateuTableOption:
|
||||
return "PathPlateuTableOption"
|
||||
case DHCPOptInterfaceMTU:
|
||||
return "InterfaceMTU"
|
||||
case DHCPOptAllSubsLocal:
|
||||
return "AllSubsLocal"
|
||||
case DHCPOptBroadcastAddr:
|
||||
return "BroadcastAddress"
|
||||
case DHCPOptMaskDiscovery:
|
||||
return "MaskDiscovery"
|
||||
case DHCPOptMaskSupplier:
|
||||
return "MaskSupplier"
|
||||
case DHCPOptRouterDiscovery:
|
||||
return "RouterDiscovery"
|
||||
case DHCPOptSolicitAddr:
|
||||
return "SolicitAddr"
|
||||
case DHCPOptStaticRoute:
|
||||
return "StaticRoute"
|
||||
case DHCPOptARPTrailers:
|
||||
return "ARPTrailers"
|
||||
case DHCPOptARPTimeout:
|
||||
return "ARPTimeout"
|
||||
case DHCPOptEthernetEncap:
|
||||
return "EthernetEncap"
|
||||
case DHCPOptTCPTTL:
|
||||
return "TCPTTL"
|
||||
case DHCPOptTCPKeepAliveInt:
|
||||
return "TCPKeepAliveInt"
|
||||
case DHCPOptTCPKeepAliveGarbage:
|
||||
return "TCPKeepAliveGarbage"
|
||||
case DHCPOptNISDomain:
|
||||
return "NISDomain"
|
||||
case DHCPOptNISServers:
|
||||
return "NISServers"
|
||||
case DHCPOptNTPServers:
|
||||
return "NTPServers"
|
||||
case DHCPOptVendorOption:
|
||||
return "VendorOption"
|
||||
case DHCPOptNetBIOSTCPNS:
|
||||
return "NetBIOSOverTCPNS"
|
||||
case DHCPOptNetBIOSTCPDDS:
|
||||
return "NetBiosOverTCPDDS"
|
||||
case DHCPOptNETBIOSTCPNodeType:
|
||||
return "NetBIOSOverTCPNodeType"
|
||||
case DHCPOptNetBIOSTCPScope:
|
||||
return "NetBIOSOverTCPScope"
|
||||
case DHCPOptXFontServer:
|
||||
return "XFontServer"
|
||||
case DHCPOptXDisplayManager:
|
||||
return "XDisplayManager"
|
||||
case DHCPOptEnd:
|
||||
return "(end)"
|
||||
case DHCPOptSIPServers:
|
||||
return "SipServers"
|
||||
case DHCPOptRequestIP:
|
||||
return "RequestIP"
|
||||
case DHCPOptLeaseTime:
|
||||
return "LeaseTime"
|
||||
case DHCPOptExtOptions:
|
||||
return "ExtOpts"
|
||||
case DHCPOptMessageType:
|
||||
return "MessageType"
|
||||
case DHCPOptServerID:
|
||||
return "ServerID"
|
||||
case DHCPOptParamsRequest:
|
||||
return "ParamsRequest"
|
||||
case DHCPOptMessage:
|
||||
return "Message"
|
||||
case DHCPOptMaxMessageSize:
|
||||
return "MaxDHCPSize"
|
||||
case DHCPOptT1:
|
||||
return "Timer1"
|
||||
case DHCPOptT2:
|
||||
return "Timer2"
|
||||
case DHCPOptClassID:
|
||||
return "ClassID"
|
||||
case DHCPOptClientID:
|
||||
return "ClientID"
|
||||
case DHCPOptDomainSearch:
|
||||
return "DomainSearch"
|
||||
case DHCPOptClasslessStaticRoute:
|
||||
return "ClasslessStaticRoute"
|
||||
default:
|
||||
return "Unknown"
|
||||
}
|
||||
}
|
||||
|
||||
// DHCPOption rerpresents a DHCP option.
|
||||
type DHCPOption struct {
|
||||
Type DHCPOpt
|
||||
Length uint8
|
||||
Data []byte
|
||||
}
|
||||
|
||||
// String returns a string version of a DHCP Option.
|
||||
func (o DHCPOption) String() string {
|
||||
switch o.Type {
|
||||
|
||||
case DHCPOptHostname, DHCPOptMeritDumpFile, DHCPOptDomainName, DHCPOptRootPath,
|
||||
DHCPOptExtensionsPath, DHCPOptNISDomain, DHCPOptNetBIOSTCPScope, DHCPOptXFontServer,
|
||||
DHCPOptXDisplayManager, DHCPOptMessage, DHCPOptDomainSearch: // string
|
||||
return fmt.Sprintf("Option(%s:%s)", o.Type, string(o.Data))
|
||||
|
||||
case DHCPOptMessageType:
|
||||
if len(o.Data) != 1 {
|
||||
return fmt.Sprintf("Option(%s:INVALID)", o.Type)
|
||||
}
|
||||
return fmt.Sprintf("Option(%s:%s)", o.Type, DHCPMsgType(o.Data[0]))
|
||||
|
||||
case DHCPOptSubnetMask, DHCPOptServerID, DHCPOptBroadcastAddr,
|
||||
DHCPOptSolicitAddr, DHCPOptRequestIP: // net.IP
|
||||
if len(o.Data) < 4 {
|
||||
return fmt.Sprintf("Option(%s:INVALID)", o.Type)
|
||||
}
|
||||
return fmt.Sprintf("Option(%s:%s)", o.Type, net.IP(o.Data))
|
||||
|
||||
case DHCPOptT1, DHCPOptT2, DHCPOptLeaseTime, DHCPOptPathMTUAgingTimeout,
|
||||
DHCPOptARPTimeout, DHCPOptTCPKeepAliveInt: // uint32
|
||||
if len(o.Data) != 4 {
|
||||
return fmt.Sprintf("Option(%s:INVALID)", o.Type)
|
||||
}
|
||||
return fmt.Sprintf("Option(%s:%d)", o.Type,
|
||||
uint32(o.Data[0])<<24|uint32(o.Data[1])<<16|uint32(o.Data[2])<<8|uint32(o.Data[3]))
|
||||
|
||||
case DHCPOptParamsRequest:
|
||||
buf := &bytes.Buffer{}
|
||||
buf.WriteString(fmt.Sprintf("Option(%s:", o.Type))
|
||||
for i, v := range o.Data {
|
||||
buf.WriteString(DHCPOpt(v).String())
|
||||
if i+1 != len(o.Data) {
|
||||
buf.WriteByte(',')
|
||||
}
|
||||
}
|
||||
buf.WriteString(")")
|
||||
return buf.String()
|
||||
|
||||
default:
|
||||
return fmt.Sprintf("Option(%s:%v)", o.Type, o.Data)
|
||||
}
|
||||
}
|
||||
|
||||
// NewDHCPOption constructs a new DHCPOption with a given type and data.
|
||||
func NewDHCPOption(t DHCPOpt, data []byte) DHCPOption {
|
||||
o := DHCPOption{Type: t}
|
||||
if data != nil {
|
||||
o.Data = data
|
||||
o.Length = uint8(len(data))
|
||||
}
|
||||
return o
|
||||
}
|
||||
|
||||
func (o *DHCPOption) encode(b []byte) error {
|
||||
switch o.Type {
|
||||
case DHCPOptPad, DHCPOptEnd:
|
||||
b[0] = byte(o.Type)
|
||||
default:
|
||||
if o.Length > 253 {
|
||||
return errors.New("data too long to encode")
|
||||
}
|
||||
b[0] = byte(o.Type)
|
||||
b[1] = o.Length
|
||||
copy(b[2:], o.Data)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o *DHCPOption) decode(data []byte) error {
|
||||
if len(data) < 1 {
|
||||
// Pad/End have a length of 1
|
||||
return errors.New("Not enough data to decode")
|
||||
}
|
||||
o.Type = DHCPOpt(data[0])
|
||||
switch o.Type {
|
||||
case DHCPOptPad, DHCPOptEnd:
|
||||
o.Data = nil
|
||||
default:
|
||||
if len(data) < 3 {
|
||||
return errors.New("Not enough data to decode")
|
||||
}
|
||||
o.Length = data[1]
|
||||
if o.Length > 253 {
|
||||
return errors.New("data too long to decode")
|
||||
}
|
||||
o.Data = data[2 : 2+o.Length]
|
||||
}
|
||||
return nil
|
||||
}
|
@ -0,0 +1,894 @@
|
||||
// Copyright 2014 Google, Inc. All rights reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the LICENSE file in the root of the source
|
||||
// tree.
|
||||
|
||||
package layers
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
|
||||
"github.com/google/gopacket"
|
||||
)
|
||||
|
||||
// DNSClass defines the class associated with a request/response. Different DNS
|
||||
// classes can be thought of as an array of parallel namespace trees.
|
||||
type DNSClass uint16
|
||||
|
||||
// DNSClass known values.
|
||||
const (
|
||||
DNSClassIN DNSClass = 1 // Internet
|
||||
DNSClassCS DNSClass = 2 // the CSNET class (Obsolete)
|
||||
DNSClassCH DNSClass = 3 // the CHAOS class
|
||||
DNSClassHS DNSClass = 4 // Hesiod [Dyer 87]
|
||||
DNSClassAny DNSClass = 255 // AnyClass
|
||||
)
|
||||
|
||||
func (dc DNSClass) String() string {
|
||||
switch dc {
|
||||
default:
|
||||
return "Unknown"
|
||||
case DNSClassIN:
|
||||
return "IN"
|
||||
case DNSClassCS:
|
||||
return "CS"
|
||||
case DNSClassCH:
|
||||
return "CH"
|
||||
case DNSClassHS:
|
||||
return "HS"
|
||||
case DNSClassAny:
|
||||
return "Any"
|
||||
}
|
||||
}
|
||||
|
||||
// DNSType defines the type of data being requested/returned in a
|
||||
// question/answer.
|
||||
type DNSType uint16
|
||||
|
||||
// DNSType known values.
|
||||
const (
|
||||
DNSTypeA DNSType = 1 // a host address
|
||||
DNSTypeNS DNSType = 2 // an authoritative name server
|
||||
DNSTypeMD DNSType = 3 // a mail destination (Obsolete - use MX)
|
||||
DNSTypeMF DNSType = 4 // a mail forwarder (Obsolete - use MX)
|
||||
DNSTypeCNAME DNSType = 5 // the canonical name for an alias
|
||||
DNSTypeSOA DNSType = 6 // marks the start of a zone of authority
|
||||
DNSTypeMB DNSType = 7 // a mailbox domain name (EXPERIMENTAL)
|
||||
DNSTypeMG DNSType = 8 // a mail group member (EXPERIMENTAL)
|
||||
DNSTypeMR DNSType = 9 // a mail rename domain name (EXPERIMENTAL)
|
||||
DNSTypeNULL DNSType = 10 // a null RR (EXPERIMENTAL)
|
||||
DNSTypeWKS DNSType = 11 // a well known service description
|
||||
DNSTypePTR DNSType = 12 // a domain name pointer
|
||||
DNSTypeHINFO DNSType = 13 // host information
|
||||
DNSTypeMINFO DNSType = 14 // mailbox or mail list information
|
||||
DNSTypeMX DNSType = 15 // mail exchange
|
||||
DNSTypeTXT DNSType = 16 // text strings
|
||||
DNSTypeAAAA DNSType = 28 // a IPv6 host address [RFC3596]
|
||||
DNSTypeSRV DNSType = 33 // server discovery [RFC2782] [RFC6195]
|
||||
)
|
||||
|
||||
func (dt DNSType) String() string {
|
||||
switch dt {
|
||||
default:
|
||||
return "Unknown"
|
||||
case DNSTypeA:
|
||||
return "A"
|
||||
case DNSTypeNS:
|
||||
return "NS"
|
||||
case DNSTypeMD:
|
||||
return "MD"
|
||||
case DNSTypeMF:
|
||||
return "MF"
|
||||
case DNSTypeCNAME:
|
||||
return "CNAME"
|
||||
case DNSTypeSOA:
|
||||
return "SOA"
|
||||
case DNSTypeMB:
|
||||
return "MB"
|
||||
case DNSTypeMG:
|
||||
return "MG"
|
||||
case DNSTypeMR:
|
||||
return "MR"
|
||||
case DNSTypeNULL:
|
||||
return "NULL"
|
||||
case DNSTypeWKS:
|
||||
return "WKS"
|
||||
case DNSTypePTR:
|
||||
return "PTR"
|
||||
case DNSTypeHINFO:
|
||||
return "HINFO"
|
||||
case DNSTypeMINFO:
|
||||
return "MINFO"
|
||||
case DNSTypeMX:
|
||||
return "MX"
|
||||
case DNSTypeTXT:
|
||||
return "TXT"
|
||||
case DNSTypeAAAA:
|
||||
return "AAAA"
|
||||
case DNSTypeSRV:
|
||||
return "SRV"
|
||||
}
|
||||
}
|
||||
|
||||
// DNSResponseCode provides response codes for question answers.
|
||||
type DNSResponseCode uint8
|
||||
|
||||
// DNSResponseCode known values.
|
||||
const (
|
||||
DNSResponseCodeNoErr DNSResponseCode = 0 // No error
|
||||
DNSResponseCodeFormErr DNSResponseCode = 1 // Format Error [RFC1035]
|
||||
DNSResponseCodeServFail DNSResponseCode = 2 // Server Failure [RFC1035]
|
||||
DNSResponseCodeNXDomain DNSResponseCode = 3 // Non-Existent Domain [RFC1035]
|
||||
DNSResponseCodeNotImp DNSResponseCode = 4 // Not Implemented [RFC1035]
|
||||
DNSResponseCodeRefused DNSResponseCode = 5 // Query Refused [RFC1035]
|
||||
DNSResponseCodeYXDomain DNSResponseCode = 6 // Name Exists when it should not [RFC2136]
|
||||
DNSResponseCodeYXRRSet DNSResponseCode = 7 // RR Set Exists when it should not [RFC2136]
|
||||
DNSResponseCodeNXRRSet DNSResponseCode = 8 // RR Set that should exist does not [RFC2136]
|
||||
DNSResponseCodeNotAuth DNSResponseCode = 9 // Server Not Authoritative for zone [RFC2136]
|
||||
DNSResponseCodeNotZone DNSResponseCode = 10 // Name not contained in zone [RFC2136]
|
||||
DNSResponseCodeBadVers DNSResponseCode = 16 // Bad OPT Version [RFC2671]
|
||||
DNSResponseCodeBadSig DNSResponseCode = 16 // TSIG Signature Failure [RFC2845]
|
||||
DNSResponseCodeBadKey DNSResponseCode = 17 // Key not recognized [RFC2845]
|
||||
DNSResponseCodeBadTime DNSResponseCode = 18 // Signature out of time window [RFC2845]
|
||||
DNSResponseCodeBadMode DNSResponseCode = 19 // Bad TKEY Mode [RFC2930]
|
||||
DNSResponseCodeBadName DNSResponseCode = 20 // Duplicate key name [RFC2930]
|
||||
DNSResponseCodeBadAlg DNSResponseCode = 21 // Algorithm not supported [RFC2930]
|
||||
DNSResponseCodeBadTruc DNSResponseCode = 22 // Bad Truncation [RFC4635]
|
||||
)
|
||||
|
||||
func (drc DNSResponseCode) String() string {
|
||||
switch drc {
|
||||
default:
|
||||
return "Unknown"
|
||||
case DNSResponseCodeNoErr:
|
||||
return "No Error"
|
||||
case DNSResponseCodeFormErr:
|
||||
return "Format Error"
|
||||
case DNSResponseCodeServFail:
|
||||
return "Server Failure "
|
||||
case DNSResponseCodeNXDomain:
|
||||
return "Non-Existent Domain"
|
||||
case DNSResponseCodeNotImp:
|
||||
return "Not Implemented"
|
||||
case DNSResponseCodeRefused:
|
||||
return "Query Refused"
|
||||
case DNSResponseCodeYXDomain:
|
||||
return "Name Exists when it should not"
|
||||
case DNSResponseCodeYXRRSet:
|
||||
return "RR Set Exists when it should not"
|
||||
case DNSResponseCodeNXRRSet:
|
||||
return "RR Set that should exist does not"
|
||||
case DNSResponseCodeNotAuth:
|
||||
return "Server Not Authoritative for zone"
|
||||
case DNSResponseCodeNotZone:
|
||||
return "Name not contained in zone"
|
||||
case DNSResponseCodeBadVers:
|
||||
return "Bad OPT Version"
|
||||
case DNSResponseCodeBadKey:
|
||||
return "Key not recognized"
|
||||
case DNSResponseCodeBadTime:
|
||||
return "Signature out of time window"
|
||||
case DNSResponseCodeBadMode:
|
||||
return "Bad TKEY Mode"
|
||||
case DNSResponseCodeBadName:
|
||||
return "Duplicate key name"
|
||||
case DNSResponseCodeBadAlg:
|
||||
return "Algorithm not supported"
|
||||
case DNSResponseCodeBadTruc:
|
||||
return "Bad Truncation"
|
||||
}
|
||||
}
|
||||
|
||||
// DNSOpCode defines a set of different operation types.
|
||||
type DNSOpCode uint8
|
||||
|
||||
// DNSOpCode known values.
|
||||
const (
|
||||
DNSOpCodeQuery DNSOpCode = 0 // Query [RFC1035]
|
||||
DNSOpCodeIQuery DNSOpCode = 1 // Inverse Query Obsolete [RFC3425]
|
||||
DNSOpCodeStatus DNSOpCode = 2 // Status [RFC1035]
|
||||
DNSOpCodeNotify DNSOpCode = 4 // Notify [RFC1996]
|
||||
DNSOpCodeUpdate DNSOpCode = 5 // Update [RFC2136]
|
||||
)
|
||||
|
||||
func (doc DNSOpCode) String() string {
|
||||
switch doc {
|
||||
default:
|
||||
return "Unknown"
|
||||
case DNSOpCodeQuery:
|
||||
return "Query"
|
||||
case DNSOpCodeIQuery:
|
||||
return "Inverse Query"
|
||||
case DNSOpCodeStatus:
|
||||
return "Status"
|
||||
case DNSOpCodeNotify:
|
||||
return "Notify"
|
||||
case DNSOpCodeUpdate:
|
||||
return "Update"
|
||||
}
|
||||
}
|
||||
|
||||
// DNS is specified in RFC 1034 / RFC 1035
|
||||
// +---------------------+
|
||||
// | Header |
|
||||
// +---------------------+
|
||||
// | Question | the question for the name server
|
||||
// +---------------------+
|
||||
// | Answer | RRs answering the question
|
||||
// +---------------------+
|
||||
// | Authority | RRs pointing toward an authority
|
||||
// +---------------------+
|
||||
// | Additional | RRs holding additional information
|
||||
// +---------------------+
|
||||
//
|
||||
// DNS Header
|
||||
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
|
||||
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
||||
// | ID |
|
||||
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
||||
// |QR| Opcode |AA|TC|RD|RA| Z | RCODE |
|
||||
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
||||
// | QDCOUNT |
|
||||
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
||||
// | ANCOUNT |
|
||||
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
||||
// | NSCOUNT |
|
||||
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
||||
// | ARCOUNT |
|
||||
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
||||
|
||||
// DNS contains data from a single Domain Name Service packet.
|
||||
type DNS struct {
|
||||
BaseLayer
|
||||
|
||||
// Header fields
|
||||
ID uint16
|
||||
QR bool
|
||||
OpCode DNSOpCode
|
||||
|
||||
AA bool // Authoritative answer
|
||||
TC bool // Truncated
|
||||
RD bool // Recursion desired
|
||||
RA bool // Recursion available
|
||||
Z uint8 // Resrved for future use
|
||||
|
||||
ResponseCode DNSResponseCode
|
||||
QDCount uint16 // Number of questions to expect
|
||||
ANCount uint16 // Number of answers to expect
|
||||
NSCount uint16 // Number of authorities to expect
|
||||
ARCount uint16 // Number of additional records to expect
|
||||
|
||||
// Entries
|
||||
Questions []DNSQuestion
|
||||
Answers []DNSResourceRecord
|
||||
Authorities []DNSResourceRecord
|
||||
Additionals []DNSResourceRecord
|
||||
|
||||
// buffer for doing name decoding. We use a single reusable buffer to avoid
|
||||
// name decoding on a single object via multiple DecodeFromBytes calls
|
||||
// requiring constant allocation of small byte slices.
|
||||
buffer []byte
|
||||
}
|
||||
|
||||
// LayerType returns gopacket.LayerTypeDNS.
|
||||
func (d *DNS) LayerType() gopacket.LayerType { return LayerTypeDNS }
|
||||
|
||||
// decodeDNS decodes the byte slice into a DNS type. It also
|
||||
// setups the application Layer in PacketBuilder.
|
||||
func decodeDNS(data []byte, p gopacket.PacketBuilder) error {
|
||||
d := &DNS{}
|
||||
err := d.DecodeFromBytes(data, p)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
p.AddLayer(d)
|
||||
p.SetApplicationLayer(d)
|
||||
return nil
|
||||
}
|
||||
|
||||
// DecodeFromBytes decodes the slice into the DNS struct.
|
||||
func (d *DNS) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
|
||||
d.buffer = d.buffer[:0]
|
||||
|
||||
if len(data) < 12 {
|
||||
df.SetTruncated()
|
||||
return errors.New("DNS packet too short")
|
||||
}
|
||||
|
||||
// since there are no further layers, the baselayer's content is
|
||||
// pointing to this layer
|
||||
d.BaseLayer = BaseLayer{Contents: data[:len(data)]}
|
||||
d.ID = binary.BigEndian.Uint16(data[:2])
|
||||
d.QR = data[2]&0x80 != 0
|
||||
d.OpCode = DNSOpCode(data[2]>>3) & 0x0F
|
||||
d.AA = data[2]&0x04 != 0
|
||||
d.TC = data[2]&0x02 != 0
|
||||
d.RD = data[2]&0x01 != 0
|
||||
d.RA = data[3]&0x80 != 0
|
||||
d.Z = uint8(data[3]>>4) & 0x7
|
||||
d.ResponseCode = DNSResponseCode(data[3] & 0xF)
|
||||
d.QDCount = binary.BigEndian.Uint16(data[4:6])
|
||||
d.ANCount = binary.BigEndian.Uint16(data[6:8])
|
||||
d.NSCount = binary.BigEndian.Uint16(data[8:10])
|
||||
d.ARCount = binary.BigEndian.Uint16(data[10:12])
|
||||
|
||||
d.Questions = d.Questions[:0]
|
||||
d.Answers = d.Answers[:0]
|
||||
d.Authorities = d.Authorities[:0]
|
||||
d.Additionals = d.Additionals[:0]
|
||||
|
||||
offset := 12
|
||||
var err error
|
||||
for i := 0; i < int(d.QDCount); i++ {
|
||||
var q DNSQuestion
|
||||
if offset, err = q.decode(data, offset, df, &d.buffer); err != nil {
|
||||
return err
|
||||
}
|
||||
d.Questions = append(d.Questions, q)
|
||||
}
|
||||
|
||||
// For some horrible reason, if we do the obvious thing in this loop:
|
||||
// var r DNSResourceRecord
|
||||
// if blah := r.decode(blah); err != nil {
|
||||
// return err
|
||||
// }
|
||||
// d.Foo = append(d.Foo, r)
|
||||
// the Go compiler thinks that 'r' escapes to the heap, causing a malloc for
|
||||
// every Answer, Authority, and Additional. To get around this, we do
|
||||
// something really silly: we append an empty resource record to our slice,
|
||||
// then use the last value in the slice to call decode. Since the value is
|
||||
// already in the slice, there's no WAY it can escape... on the other hand our
|
||||
// code is MUCH uglier :(
|
||||
for i := 0; i < int(d.ANCount); i++ {
|
||||
d.Answers = append(d.Answers, DNSResourceRecord{})
|
||||
if offset, err = d.Answers[i].decode(data, offset, df, &d.buffer); err != nil {
|
||||
d.Answers = d.Answers[:i] // strip off erroneous value
|
||||
return err
|
||||
}
|
||||
}
|
||||
for i := 0; i < int(d.NSCount); i++ {
|
||||
d.Authorities = append(d.Authorities, DNSResourceRecord{})
|
||||
if offset, err = d.Authorities[i].decode(data, offset, df, &d.buffer); err != nil {
|
||||
d.Authorities = d.Authorities[:i] // strip off erroneous value
|
||||
return err
|
||||
}
|
||||
}
|
||||
for i := 0; i < int(d.ARCount); i++ {
|
||||
d.Additionals = append(d.Additionals, DNSResourceRecord{})
|
||||
if offset, err = d.Additionals[i].decode(data, offset, df, &d.buffer); err != nil {
|
||||
d.Additionals = d.Additionals[:i] // strip off erroneous value
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if uint16(len(d.Questions)) != d.QDCount {
|
||||
return errors.New("Invalid query decoding, not the right number of questions")
|
||||
} else if uint16(len(d.Answers)) != d.ANCount {
|
||||
return errors.New("Invalid query decoding, not the right number of answers")
|
||||
} else if uint16(len(d.Authorities)) != d.NSCount {
|
||||
return errors.New("Invalid query decoding, not the right number of authorities")
|
||||
} else if uint16(len(d.Additionals)) != d.ARCount {
|
||||
return errors.New("Invalid query decoding, not the right number of additionals info")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// CanDecode implements gopacket.DecodingLayer.
|
||||
func (d *DNS) CanDecode() gopacket.LayerClass {
|
||||
return LayerTypeDNS
|
||||
}
|
||||
|
||||
// NextLayerType implements gopacket.DecodingLayer.
|
||||
func (d *DNS) NextLayerType() gopacket.LayerType {
|
||||
return gopacket.LayerTypePayload
|
||||
}
|
||||
|
||||
// Payload returns nil.
|
||||
func (d *DNS) Payload() []byte {
|
||||
return nil
|
||||
}
|
||||
|
||||
func b2i(b bool) int {
|
||||
if b {
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func recSize(rr *DNSResourceRecord) int {
|
||||
switch rr.Type {
|
||||
case DNSTypeA:
|
||||
return 4
|
||||
case DNSTypeAAAA:
|
||||
return 16
|
||||
case DNSTypeNS:
|
||||
return len(rr.NS) + 2
|
||||
case DNSTypeCNAME:
|
||||
return len(rr.CNAME) + 2
|
||||
case DNSTypePTR:
|
||||
return len(rr.PTR) + 2
|
||||
case DNSTypeSOA:
|
||||
return len(rr.SOA.MName) + 2 + len(rr.SOA.RName) + 2 + 20
|
||||
case DNSTypeMX:
|
||||
return 2 + len(rr.MX.Name) + 2
|
||||
case DNSTypeTXT:
|
||||
l := len(rr.TXTs)
|
||||
for _, txt := range rr.TXTs {
|
||||
l += len(txt)
|
||||
}
|
||||
return l
|
||||
case DNSTypeSRV:
|
||||
return 6 + len(rr.SRV.Name) + 2
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
func computeSize(recs []DNSResourceRecord) int {
|
||||
sz := 0
|
||||
for _, rr := range recs {
|
||||
sz += len(rr.Name) + 14
|
||||
sz += recSize(&rr)
|
||||
}
|
||||
return sz
|
||||
}
|
||||
|
||||
// SerializeTo writes the serialized form of this layer into the
|
||||
// SerializationBuffer, implementing gopacket.SerializableLayer.
|
||||
func (d *DNS) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
|
||||
dsz := 0
|
||||
for _, q := range d.Questions {
|
||||
dsz += len(q.Name) + 6
|
||||
}
|
||||
dsz += computeSize(d.Answers)
|
||||
dsz += computeSize(d.Authorities)
|
||||
dsz += computeSize(d.Additionals)
|
||||
|
||||
bytes, err := b.PrependBytes(12 + dsz)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
binary.BigEndian.PutUint16(bytes, d.ID)
|
||||
bytes[2] = byte((b2i(d.QR) << 7) | (int(d.OpCode) << 3) | (b2i(d.AA) << 2) | (b2i(d.TC) << 1) | b2i(d.RD))
|
||||
bytes[3] = byte((b2i(d.RA) << 7) | (int(d.Z) << 4) | int(d.ResponseCode))
|
||||
|
||||
if opts.FixLengths {
|
||||
d.QDCount = uint16(len(d.Questions))
|
||||
d.ANCount = uint16(len(d.Answers))
|
||||
d.NSCount = uint16(len(d.Authorities))
|
||||
d.ARCount = uint16(len(d.Additionals))
|
||||
}
|
||||
binary.BigEndian.PutUint16(bytes[4:], d.QDCount)
|
||||
binary.BigEndian.PutUint16(bytes[6:], d.ANCount)
|
||||
binary.BigEndian.PutUint16(bytes[8:], d.NSCount)
|
||||
binary.BigEndian.PutUint16(bytes[10:], d.ARCount)
|
||||
|
||||
off := 12
|
||||
for _, qd := range d.Questions {
|
||||
n := qd.encode(bytes, off)
|
||||
off += n
|
||||
}
|
||||
|
||||
for i := range d.Answers {
|
||||
// done this way so we can modify DNSResourceRecord to fix
|
||||
// lengths if requested
|
||||
qa := &d.Answers[i]
|
||||
n, err := qa.encode(bytes, off, opts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
off += n
|
||||
}
|
||||
|
||||
for i := range d.Authorities {
|
||||
qa := &d.Authorities[i]
|
||||
n, err := qa.encode(bytes, off, opts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
off += n
|
||||
}
|
||||
for i := range d.Additionals {
|
||||
qa := &d.Additionals[i]
|
||||
n, err := qa.encode(bytes, off, opts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
off += n
|
||||
}
|
||||
|
||||
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")
|
||||
} else if offset < 0 {
|
||||
return nil, 0, errors.New("dns name offset is negative")
|
||||
}
|
||||
start := len(*buffer)
|
||||
index := offset
|
||||
if data[index] == 0x00 {
|
||||
return nil, index + 1, nil
|
||||
}
|
||||
loop:
|
||||
for data[index] != 0x00 {
|
||||
switch data[index] & 0xc0 {
|
||||
default:
|
||||
/* RFC 1035
|
||||
A domain name represented as a sequence of labels, where
|
||||
each label consists of a length octet followed by that
|
||||
number of octets. The domain name terminates with the
|
||||
zero length octet for the null label of the root. Note
|
||||
that this field may be an odd number of octets; no
|
||||
padding is used.
|
||||
*/
|
||||
index2 := index + int(data[index]) + 1
|
||||
if index2-offset > 255 {
|
||||
return nil, 0, errors.New("dns name is too long")
|
||||
} else if index2 < index+1 || index2 > len(data) {
|
||||
return nil, 0, errors.New("dns name uncomputable: invalid index")
|
||||
}
|
||||
*buffer = append(*buffer, '.')
|
||||
*buffer = append(*buffer, data[index+1:index2]...)
|
||||
index = index2
|
||||
|
||||
case 0xc0:
|
||||
/* RFC 1035
|
||||
The pointer takes the form of a two octet sequence.
|
||||
|
||||
The first two bits are ones. This allows a pointer to
|
||||
be distinguished from a label, since the label must
|
||||
begin with two zero bits because labels are restricted
|
||||
to 63 octets or less. (The 10 and 01 combinations are
|
||||
reserved for future use.) The OFFSET field specifies
|
||||
an offset from the start of the message (i.e., the
|
||||
first octet of the ID field in the domain header). A
|
||||
zero offset specifies the first byte of the ID field,
|
||||
etc.
|
||||
|
||||
The compression scheme allows a domain name in a message to be
|
||||
represented as either:
|
||||
- a sequence of labels ending in a zero octet
|
||||
- a pointer
|
||||
- a sequence of labels ending with a pointer
|
||||
*/
|
||||
if index+2 > len(data) {
|
||||
return nil, 0, errors.New("dns offset pointer too high")
|
||||
}
|
||||
offsetp := int(binary.BigEndian.Uint16(data[index:index+2]) & 0x3fff)
|
||||
if offsetp > len(data) {
|
||||
return nil, 0, errors.New("dns offset pointer too high")
|
||||
}
|
||||
// This looks a little tricky, but actually isn't. Because of how
|
||||
// decodeName is written, calling it appends the decoded name to the
|
||||
// current buffer. We already have the start of the buffer, then, so
|
||||
// once this call is done buffer[start:] will contain our full name.
|
||||
_, _, err := decodeName(data, offsetp, buffer, level+1)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
index++ // pointer is two bytes, so add an extra byte here.
|
||||
break loop
|
||||
/* EDNS, or other DNS option ? */
|
||||
case 0x40: // RFC 2673
|
||||
return nil, 0, fmt.Errorf("qname '0x40' - RFC 2673 unsupported yet (data=%x index=%d)",
|
||||
data[index], index)
|
||||
|
||||
case 0x80:
|
||||
return nil, 0, fmt.Errorf("qname '0x80' unsupported yet (data=%x index=%d)",
|
||||
data[index], index)
|
||||
}
|
||||
if index >= len(data) {
|
||||
return nil, 0, errors.New("dns index walked out of range")
|
||||
}
|
||||
}
|
||||
if len(*buffer) <= start {
|
||||
return nil, 0, errors.New("no dns data found for name")
|
||||
}
|
||||
return (*buffer)[start+1:], index + 1, nil
|
||||
}
|
||||
|
||||
// DNSQuestion wraps a single request (question) within a DNS query.
|
||||
type DNSQuestion struct {
|
||||
Name []byte
|
||||
Type DNSType
|
||||
Class DNSClass
|
||||
}
|
||||
|
||||
func (q *DNSQuestion) decode(data []byte, offset int, df gopacket.DecodeFeedback, buffer *[]byte) (int, error) {
|
||||
name, endq, err := decodeName(data, offset, buffer, 1)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
q.Name = name
|
||||
q.Type = DNSType(binary.BigEndian.Uint16(data[endq : endq+2]))
|
||||
q.Class = DNSClass(binary.BigEndian.Uint16(data[endq+2 : endq+4]))
|
||||
|
||||
return endq + 4, nil
|
||||
}
|
||||
|
||||
func (q *DNSQuestion) encode(data []byte, offset int) int {
|
||||
noff := encodeName(q.Name, data, offset)
|
||||
binary.BigEndian.PutUint16(data[noff:], uint16(q.Type))
|
||||
binary.BigEndian.PutUint16(data[noff+2:], uint16(q.Class))
|
||||
return len(q.Name) + 6
|
||||
}
|
||||
|
||||
// DNSResourceRecord
|
||||
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
|
||||
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
||||
// | |
|
||||
// / /
|
||||
// / NAME /
|
||||
// | |
|
||||
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
||||
// | TYPE |
|
||||
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
||||
// | CLASS |
|
||||
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
||||
// | TTL |
|
||||
// | |
|
||||
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
||||
// | RDLENGTH |
|
||||
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--|
|
||||
// / RDATA /
|
||||
// / /
|
||||
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
||||
|
||||
// DNSResourceRecord wraps the data from a single DNS resource within a
|
||||
// response.
|
||||
type DNSResourceRecord struct {
|
||||
// Header
|
||||
Name []byte
|
||||
Type DNSType
|
||||
Class DNSClass
|
||||
TTL uint32
|
||||
|
||||
// RDATA Raw Values
|
||||
DataLength uint16
|
||||
Data []byte
|
||||
|
||||
// RDATA Decoded Values
|
||||
IP net.IP
|
||||
NS, CNAME, PTR []byte
|
||||
TXTs [][]byte
|
||||
SOA DNSSOA
|
||||
SRV DNSSRV
|
||||
MX DNSMX
|
||||
|
||||
// Undecoded TXT for backward compatibility
|
||||
TXT []byte
|
||||
}
|
||||
|
||||
// decode decodes the resource record, returning the total length of the record.
|
||||
func (rr *DNSResourceRecord) decode(data []byte, offset int, df gopacket.DecodeFeedback, buffer *[]byte) (int, error) {
|
||||
name, endq, err := decodeName(data, offset, buffer, 1)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
rr.Name = name
|
||||
rr.Type = DNSType(binary.BigEndian.Uint16(data[endq : endq+2]))
|
||||
rr.Class = DNSClass(binary.BigEndian.Uint16(data[endq+2 : endq+4]))
|
||||
rr.TTL = binary.BigEndian.Uint32(data[endq+4 : endq+8])
|
||||
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")
|
||||
}
|
||||
rr.Data = data[endq+10 : end]
|
||||
|
||||
if err = rr.decodeRData(data, endq+10, buffer); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return endq + 10 + int(rr.DataLength), nil
|
||||
}
|
||||
|
||||
func encodeName(name []byte, data []byte, offset int) int {
|
||||
l := 0
|
||||
for i := range name {
|
||||
if name[i] == '.' {
|
||||
data[offset+i-l] = byte(l)
|
||||
l = 0
|
||||
} else {
|
||||
// skip one to write the length
|
||||
data[offset+i+1] = name[i]
|
||||
l++
|
||||
}
|
||||
}
|
||||
// length for final portion
|
||||
data[offset+len(name)-l] = byte(l)
|
||||
data[offset+len(name)+1] = 0x00 // terminal
|
||||
return offset + len(name) + 2
|
||||
}
|
||||
|
||||
func (rr *DNSResourceRecord) encode(data []byte, offset int, opts gopacket.SerializeOptions) (int, error) {
|
||||
|
||||
noff := encodeName(rr.Name, data, offset)
|
||||
|
||||
binary.BigEndian.PutUint16(data[noff:], uint16(rr.Type))
|
||||
binary.BigEndian.PutUint16(data[noff+2:], uint16(rr.Class))
|
||||
binary.BigEndian.PutUint32(data[noff+4:], uint32(rr.TTL))
|
||||
|
||||
switch rr.Type {
|
||||
case DNSTypeA:
|
||||
copy(data[noff+10:], rr.IP.To4())
|
||||
case DNSTypeAAAA:
|
||||
copy(data[noff+10:], rr.IP)
|
||||
case DNSTypeNS:
|
||||
encodeName(rr.NS, data, noff+10)
|
||||
case DNSTypeCNAME:
|
||||
encodeName(rr.CNAME, data, noff+10)
|
||||
case DNSTypePTR:
|
||||
encodeName(rr.PTR, data, noff+10)
|
||||
case DNSTypeSOA:
|
||||
noff2 := encodeName(rr.SOA.MName, data, noff+10)
|
||||
noff2 = encodeName(rr.SOA.RName, data, noff2)
|
||||
binary.BigEndian.PutUint32(data[noff2:], rr.SOA.Serial)
|
||||
binary.BigEndian.PutUint32(data[noff2+4:], rr.SOA.Refresh)
|
||||
binary.BigEndian.PutUint32(data[noff2+8:], rr.SOA.Retry)
|
||||
binary.BigEndian.PutUint32(data[noff2+12:], rr.SOA.Expire)
|
||||
binary.BigEndian.PutUint32(data[noff2+16:], rr.SOA.Minimum)
|
||||
case DNSTypeMX:
|
||||
binary.BigEndian.PutUint16(data[noff+10:], rr.MX.Preference)
|
||||
encodeName(rr.MX.Name, data, noff+12)
|
||||
case DNSTypeTXT:
|
||||
noff2 := noff + 10
|
||||
for _, txt := range rr.TXTs {
|
||||
data[noff2] = byte(len(txt))
|
||||
copy(data[noff2+1:], txt)
|
||||
noff2 += 1 + len(txt)
|
||||
}
|
||||
case DNSTypeSRV:
|
||||
binary.BigEndian.PutUint16(data[noff+10:], rr.SRV.Priority)
|
||||
binary.BigEndian.PutUint16(data[noff+12:], rr.SRV.Weight)
|
||||
binary.BigEndian.PutUint16(data[noff+14:], rr.SRV.Port)
|
||||
encodeName(rr.SRV.Name, data, noff+16)
|
||||
default:
|
||||
return 0, fmt.Errorf("serializing resource record of type %v not supported", rr.Type)
|
||||
}
|
||||
|
||||
// DataLength
|
||||
dSz := recSize(rr)
|
||||
binary.BigEndian.PutUint16(data[noff+8:], uint16(dSz))
|
||||
|
||||
if opts.FixLengths {
|
||||
rr.DataLength = uint16(dSz)
|
||||
}
|
||||
|
||||
return len(rr.Name) + 1 + 11 + dSz, nil
|
||||
}
|
||||
|
||||
func (rr *DNSResourceRecord) String() string {
|
||||
|
||||
if rr.Class == DNSClassIN {
|
||||
switch rr.Type {
|
||||
case DNSTypeA, DNSTypeAAAA:
|
||||
return rr.IP.String()
|
||||
case DNSTypeNS:
|
||||
return "NS " + string(rr.NS)
|
||||
case DNSTypeCNAME:
|
||||
return "CNAME " + string(rr.CNAME)
|
||||
case DNSTypePTR:
|
||||
return "PTR " + string(rr.PTR)
|
||||
case DNSTypeTXT:
|
||||
return "TXT " + string(rr.TXT)
|
||||
}
|
||||
}
|
||||
|
||||
return fmt.Sprintf("<%v, %v>", rr.Class, rr.Type)
|
||||
}
|
||||
|
||||
func decodeCharacterStrings(data []byte) ([][]byte, error) {
|
||||
strings := make([][]byte, 0, 1)
|
||||
end := len(data)
|
||||
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>")
|
||||
}
|
||||
strings = append(strings, data[index+1:index2])
|
||||
}
|
||||
return strings, nil
|
||||
}
|
||||
|
||||
func (rr *DNSResourceRecord) decodeRData(data []byte, offset int, buffer *[]byte) error {
|
||||
switch rr.Type {
|
||||
case DNSTypeA:
|
||||
rr.IP = rr.Data
|
||||
case DNSTypeAAAA:
|
||||
rr.IP = rr.Data
|
||||
case DNSTypeTXT, DNSTypeHINFO:
|
||||
rr.TXT = rr.Data
|
||||
txts, err := decodeCharacterStrings(rr.Data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
rr.TXTs = txts
|
||||
case DNSTypeNS:
|
||||
name, _, err := decodeName(data, offset, buffer, 1)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
rr.NS = name
|
||||
case DNSTypeCNAME:
|
||||
name, _, err := decodeName(data, offset, buffer, 1)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
rr.CNAME = name
|
||||
case DNSTypePTR:
|
||||
name, _, err := decodeName(data, offset, buffer, 1)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
rr.PTR = name
|
||||
case DNSTypeSOA:
|
||||
name, endq, err := decodeName(data, offset, buffer, 1)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
rr.SOA.MName = name
|
||||
name, endq, err = decodeName(data, endq, buffer, 1)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
rr.SOA.RName = name
|
||||
rr.SOA.Serial = binary.BigEndian.Uint32(data[endq : endq+4])
|
||||
rr.SOA.Refresh = binary.BigEndian.Uint32(data[endq+4 : endq+8])
|
||||
rr.SOA.Retry = binary.BigEndian.Uint32(data[endq+8 : endq+12])
|
||||
rr.SOA.Expire = binary.BigEndian.Uint32(data[endq+12 : endq+16])
|
||||
rr.SOA.Minimum = binary.BigEndian.Uint32(data[endq+16 : endq+20])
|
||||
case DNSTypeMX:
|
||||
rr.MX.Preference = binary.BigEndian.Uint16(data[offset : offset+2])
|
||||
name, _, err := decodeName(data, offset+2, buffer, 1)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
rr.MX.Name = name
|
||||
case DNSTypeSRV:
|
||||
rr.SRV.Priority = binary.BigEndian.Uint16(data[offset : offset+2])
|
||||
rr.SRV.Weight = binary.BigEndian.Uint16(data[offset+2 : offset+4])
|
||||
rr.SRV.Port = binary.BigEndian.Uint16(data[offset+4 : offset+6])
|
||||
name, _, err := decodeName(data, offset+6, buffer, 1)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
rr.SRV.Name = name
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DNSSOA is a Start of Authority record. Each domain requires a SOA record at
|
||||
// the cutover where a domain is delegated from its parent.
|
||||
type DNSSOA struct {
|
||||
MName, RName []byte
|
||||
Serial, Refresh, Retry, Expire, Minimum uint32
|
||||
}
|
||||
|
||||
// DNSSRV is a Service record, defining a location (hostname/port) of a
|
||||
// server/service.
|
||||
type DNSSRV struct {
|
||||
Priority, Weight, Port uint16
|
||||
Name []byte
|
||||
}
|
||||
|
||||
// DNSMX is a mail exchange record, defining a mail server for a recipient's
|
||||
// domain.
|
||||
type DNSMX struct {
|
||||
Preference uint16
|
||||
Name []byte
|
||||
}
|
@ -0,0 +1,61 @@
|
||||
// Copyright 2012 Google, Inc. All rights reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the LICENSE file in the root of the source
|
||||
// tree.
|
||||
|
||||
/*
|
||||
Package layers provides decoding layers for many common protocols.
|
||||
|
||||
The layers package contains decode implementations for a number of different
|
||||
types of packet layers. Users of gopacket will almost always want to also use
|
||||
layers to actually decode packet data into useful pieces. To see the set of
|
||||
protocols that gopacket/layers is currently able to decode,
|
||||
look at the set of LayerTypes defined in the Variables sections. The
|
||||
layers package also defines endpoints for many of the common packet layers
|
||||
that have source/destination addresses associated with them, for example IPv4/6
|
||||
(IPs) and TCP/UDP (ports).
|
||||
Finally, layers contains a number of useful enumerations (IPProtocol,
|
||||
EthernetType, LinkType, PPPType, etc...). Many of these implement the
|
||||
gopacket.Decoder interface, so they can be passed into gopacket as decoders.
|
||||
|
||||
Most common protocol layers are named using acronyms or other industry-common
|
||||
names (IPv4, TCP, PPP). Some of the less common ones have their names expanded
|
||||
(CiscoDiscoveryProtocol).
|
||||
For certain protocols, sub-parts of the protocol are split out into their own
|
||||
layers (SCTP, for example). This is done mostly in cases where portions of the
|
||||
protocol may fulfill the capabilities of interesting layers (SCTPData implements
|
||||
ApplicationLayer, while base SCTP implements TransportLayer), or possibly
|
||||
because splitting a protocol into a few layers makes decoding easier.
|
||||
|
||||
This package is meant to be used with its parent,
|
||||
http://github.com/google/gopacket.
|
||||
|
||||
Port Types
|
||||
|
||||
Instead of using raw uint16 or uint8 values for ports, we use a different port
|
||||
type for every protocol, for example TCPPort and UDPPort. This allows us to
|
||||
override string behavior for each port, which we do by setting up port name
|
||||
maps (TCPPortNames, UDPPortNames, etc...). Well-known ports are annotated with
|
||||
their protocol names, and their String function displays these names:
|
||||
|
||||
p := TCPPort(80)
|
||||
fmt.Printf("Number: %d String: %v", p, p)
|
||||
// Prints: "Number: 80 String: 80(http)"
|
||||
|
||||
Modifying Decode Behavior
|
||||
|
||||
layers links together decoding through its enumerations. For example, after
|
||||
decoding layer type Ethernet, it uses Ethernet.EthernetType as its next decoder.
|
||||
All enumerations that act as decoders, like EthernetType, can be modified by
|
||||
users depending on their preferences. For example, if you have a spiffy new
|
||||
IPv4 decoder that works way better than the one built into layers, you can do
|
||||
this:
|
||||
|
||||
var mySpiffyIPv4Decoder gopacket.Decoder = ...
|
||||
layers.EthernetTypeMetadata[EthernetTypeIPv4].DecodeWith = mySpiffyIPv4Decoder
|
||||
|
||||
This will make all future ethernet packets use your new decoder to decode IPv4
|
||||
packets, instead of the built-in decoder used by gopacket.
|
||||
*/
|
||||
package layers
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,71 @@
|
||||
// Copyright 2012 Google, Inc. All rights reserved.
|
||||
// Copyright 2009-2011 Andreas Krennmair. 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"
|
||||
"fmt"
|
||||
"github.com/google/gopacket"
|
||||
)
|
||||
|
||||
// Dot1Q is the packet layer for 802.1Q VLAN headers.
|
||||
type Dot1Q struct {
|
||||
BaseLayer
|
||||
Priority uint8
|
||||
DropEligible bool
|
||||
VLANIdentifier uint16
|
||||
Type EthernetType
|
||||
}
|
||||
|
||||
// LayerType returns gopacket.LayerTypeDot1Q
|
||||
func (d *Dot1Q) LayerType() gopacket.LayerType { return LayerTypeDot1Q }
|
||||
|
||||
// DecodeFromBytes decodes the given bytes into this layer.
|
||||
func (d *Dot1Q) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
|
||||
d.Priority = (data[0] & 0xE0) >> 5
|
||||
d.DropEligible = data[0]&0x10 != 0
|
||||
d.VLANIdentifier = binary.BigEndian.Uint16(data[:2]) & 0x0FFF
|
||||
d.Type = EthernetType(binary.BigEndian.Uint16(data[2:4]))
|
||||
d.BaseLayer = BaseLayer{Contents: data[:4], Payload: data[4:]}
|
||||
return nil
|
||||
}
|
||||
|
||||
// CanDecode returns the set of layer types that this DecodingLayer can decode.
|
||||
func (d *Dot1Q) CanDecode() gopacket.LayerClass {
|
||||
return LayerTypeDot1Q
|
||||
}
|
||||
|
||||
// NextLayerType returns the layer type contained by this DecodingLayer.
|
||||
func (d *Dot1Q) NextLayerType() gopacket.LayerType {
|
||||
return d.Type.LayerType()
|
||||
}
|
||||
|
||||
func decodeDot1Q(data []byte, p gopacket.PacketBuilder) error {
|
||||
d := &Dot1Q{}
|
||||
return decodingLayerDecoder(d, data, p)
|
||||
}
|
||||
|
||||
// SerializeTo writes the serialized form of this layer into the
|
||||
// SerializationBuffer, implementing gopacket.SerializableLayer.
|
||||
// See the docs for gopacket.SerializableLayer for more info.
|
||||
func (d *Dot1Q) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
|
||||
bytes, err := b.PrependBytes(4)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if d.VLANIdentifier > 0xFFF {
|
||||
return fmt.Errorf("vlan identifier %v is too high", d.VLANIdentifier)
|
||||
}
|
||||
firstBytes := uint16(d.Priority)<<13 | d.VLANIdentifier
|
||||
if d.DropEligible {
|
||||
firstBytes |= 0x1000
|
||||
}
|
||||
binary.BigEndian.PutUint16(bytes, firstBytes)
|
||||
binary.BigEndian.PutUint16(bytes[2:], uint16(d.Type))
|
||||
return nil
|
||||
}
|
@ -0,0 +1,106 @@
|
||||
// Copyright 2012 Google, Inc. All rights reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the LICENSE file in the root of the source
|
||||
// tree.
|
||||
|
||||
package layers
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"github.com/google/gopacket"
|
||||
)
|
||||
|
||||
type EAPCode uint8
|
||||
type EAPType uint8
|
||||
|
||||
const (
|
||||
EAPCodeRequest EAPCode = 1
|
||||
EAPCodeResponse EAPCode = 2
|
||||
EAPCodeSuccess EAPCode = 3
|
||||
EAPCodeFailure EAPCode = 4
|
||||
|
||||
// EAPTypeNone means that this EAP layer has no Type or TypeData.
|
||||
// Success and Failure EAPs will have this set.
|
||||
EAPTypeNone EAPType = 0
|
||||
|
||||
EAPTypeIdentity EAPType = 1
|
||||
EAPTypeNotification EAPType = 2
|
||||
EAPTypeNACK EAPType = 3
|
||||
EAPTypeOTP EAPType = 4
|
||||
EAPTypeTokenCard EAPType = 5
|
||||
)
|
||||
|
||||
// EAP defines an Extensible Authentication Protocol (rfc 3748) layer.
|
||||
type EAP struct {
|
||||
BaseLayer
|
||||
Code EAPCode
|
||||
Id uint8
|
||||
Length uint16
|
||||
Type EAPType
|
||||
TypeData []byte
|
||||
}
|
||||
|
||||
// LayerType returns LayerTypeEAP.
|
||||
func (e *EAP) LayerType() gopacket.LayerType { return LayerTypeEAP }
|
||||
|
||||
// DecodeFromBytes decodes the given bytes into this layer.
|
||||
func (e *EAP) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
|
||||
e.Code = EAPCode(data[0])
|
||||
e.Id = data[1]
|
||||
e.Length = binary.BigEndian.Uint16(data[2:4])
|
||||
switch {
|
||||
case e.Length > 4:
|
||||
e.Type = EAPType(data[4])
|
||||
e.TypeData = data[5:]
|
||||
case e.Length == 4:
|
||||
e.Type = 0
|
||||
e.TypeData = nil
|
||||
default:
|
||||
return fmt.Errorf("invalid EAP length %d", e.Length)
|
||||
}
|
||||
e.BaseLayer.Contents = data[:e.Length]
|
||||
e.BaseLayer.Payload = data[e.Length:] // Should be 0 bytes
|
||||
return nil
|
||||
}
|
||||
|
||||
// SerializeTo writes the serialized form of this layer into the
|
||||
// SerializationBuffer, implementing gopacket.SerializableLayer.
|
||||
// See the docs for gopacket.SerializableLayer for more info.
|
||||
func (e *EAP) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
|
||||
if opts.FixLengths {
|
||||
e.Length = uint16(len(e.TypeData) + 1)
|
||||
}
|
||||
size := len(e.TypeData) + 4
|
||||
if size > 4 {
|
||||
size++
|
||||
}
|
||||
bytes, err := b.PrependBytes(size)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
bytes[0] = byte(e.Code)
|
||||
bytes[1] = e.Id
|
||||
binary.BigEndian.PutUint16(bytes[2:], e.Length)
|
||||
if size > 4 {
|
||||
bytes[4] = byte(e.Type)
|
||||
copy(bytes[5:], e.TypeData)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// CanDecode returns the set of layer types that this DecodingLayer can decode.
|
||||
func (e *EAP) CanDecode() gopacket.LayerClass {
|
||||
return LayerTypeEAP
|
||||
}
|
||||
|
||||
// NextLayerType returns the layer type contained by this DecodingLayer.
|
||||
func (e *EAP) NextLayerType() gopacket.LayerType {
|
||||
return gopacket.LayerTypeZero
|
||||
}
|
||||
|
||||
func decodeEAP(data []byte, p gopacket.PacketBuilder) error {
|
||||
e := &EAP{}
|
||||
return decodingLayerDecoder(e, data, p)
|
||||
}
|
@ -0,0 +1,57 @@
|
||||
// Copyright 2012 Google, Inc. All rights reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the LICENSE file in the root of the source
|
||||
// tree.
|
||||
|
||||
package layers
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"github.com/google/gopacket"
|
||||
)
|
||||
|
||||
// EAPOL defines an EAP over LAN (802.1x) layer.
|
||||
type EAPOL struct {
|
||||
BaseLayer
|
||||
Version uint8
|
||||
Type EAPOLType
|
||||
Length uint16
|
||||
}
|
||||
|
||||
// LayerType returns LayerTypeEAPOL.
|
||||
func (e *EAPOL) LayerType() gopacket.LayerType { return LayerTypeEAPOL }
|
||||
|
||||
// DecodeFromBytes decodes the given bytes into this layer.
|
||||
func (e *EAPOL) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
|
||||
e.Version = data[0]
|
||||
e.Type = EAPOLType(data[1])
|
||||
e.Length = binary.BigEndian.Uint16(data[2:4])
|
||||
e.BaseLayer = BaseLayer{data[:4], data[4:]}
|
||||
return nil
|
||||
}
|
||||
|
||||
// SerializeTo writes the serialized form of this layer into the
|
||||
// SerializationBuffer, implementing gopacket.SerializableLayer
|
||||
func (e *EAPOL) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
|
||||
bytes, _ := b.PrependBytes(4)
|
||||
bytes[0] = e.Version
|
||||
bytes[1] = byte(e.Type)
|
||||
binary.BigEndian.PutUint16(bytes[2:], e.Length)
|
||||
return nil
|
||||
}
|
||||
|
||||
// CanDecode returns the set of layer types that this DecodingLayer can decode.
|
||||
func (e *EAPOL) CanDecode() gopacket.LayerClass {
|
||||
return LayerTypeEAPOL
|
||||
}
|
||||
|
||||
// NextLayerType returns the layer type contained by this DecodingLayer.
|
||||
func (e *EAPOL) NextLayerType() gopacket.LayerType {
|
||||
return e.Type.LayerType()
|
||||
}
|
||||
|
||||
func decodeEAPOL(data []byte, p gopacket.PacketBuilder) error {
|
||||
e := &EAPOL{}
|
||||
return decodingLayerDecoder(e, data, p)
|
||||
}
|
@ -0,0 +1,97 @@
|
||||
// Copyright 2012 Google, Inc. All rights reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the LICENSE file in the root of the source
|
||||
// tree.
|
||||
|
||||
package layers
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"github.com/google/gopacket"
|
||||
"net"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
var (
|
||||
// We use two different endpoint types for IPv4 vs IPv6 addresses, so that
|
||||
// ordering with endpointA.LessThan(endpointB) sanely groups all IPv4
|
||||
// addresses and all IPv6 addresses, such that IPv6 > IPv4 for all addresses.
|
||||
EndpointIPv4 = gopacket.RegisterEndpointType(1, gopacket.EndpointTypeMetadata{Name: "IPv4", Formatter: func(b []byte) string {
|
||||
return net.IP(b).String()
|
||||
}})
|
||||
EndpointIPv6 = gopacket.RegisterEndpointType(2, gopacket.EndpointTypeMetadata{Name: "IPv6", Formatter: func(b []byte) string {
|
||||
return net.IP(b).String()
|
||||
}})
|
||||
|
||||
EndpointMAC = gopacket.RegisterEndpointType(3, gopacket.EndpointTypeMetadata{Name: "MAC", Formatter: func(b []byte) string {
|
||||
return net.HardwareAddr(b).String()
|
||||
}})
|
||||
EndpointTCPPort = gopacket.RegisterEndpointType(4, gopacket.EndpointTypeMetadata{Name: "TCP", Formatter: func(b []byte) string {
|
||||
return strconv.Itoa(int(binary.BigEndian.Uint16(b)))
|
||||
}})
|
||||
EndpointUDPPort = gopacket.RegisterEndpointType(5, gopacket.EndpointTypeMetadata{Name: "UDP", Formatter: func(b []byte) string {
|
||||
return strconv.Itoa(int(binary.BigEndian.Uint16(b)))
|
||||
}})
|
||||
EndpointSCTPPort = gopacket.RegisterEndpointType(6, gopacket.EndpointTypeMetadata{Name: "SCTP", Formatter: func(b []byte) string {
|
||||
return strconv.Itoa(int(binary.BigEndian.Uint16(b)))
|
||||
}})
|
||||
EndpointRUDPPort = gopacket.RegisterEndpointType(7, gopacket.EndpointTypeMetadata{Name: "RUDP", Formatter: func(b []byte) string {
|
||||
return strconv.Itoa(int(b[0]))
|
||||
}})
|
||||
EndpointUDPLitePort = gopacket.RegisterEndpointType(8, gopacket.EndpointTypeMetadata{Name: "UDPLite", Formatter: func(b []byte) string {
|
||||
return strconv.Itoa(int(binary.BigEndian.Uint16(b)))
|
||||
}})
|
||||
EndpointPPP = gopacket.RegisterEndpointType(9, gopacket.EndpointTypeMetadata{Name: "PPP", Formatter: func([]byte) string {
|
||||
return "point"
|
||||
}})
|
||||
)
|
||||
|
||||
// NewIPEndpoint creates a new IP (v4 or v6) endpoint from a net.IP address.
|
||||
// It returns gopacket.InvalidEndpoint if the IP address is invalid.
|
||||
func NewIPEndpoint(a net.IP) gopacket.Endpoint {
|
||||
ipv4 := a.To4()
|
||||
if ipv4 != nil {
|
||||
return gopacket.NewEndpoint(EndpointIPv4, []byte(ipv4))
|
||||
}
|
||||
|
||||
ipv6 := a.To16()
|
||||
if ipv6 != nil {
|
||||
return gopacket.NewEndpoint(EndpointIPv6, []byte(ipv6))
|
||||
}
|
||||
|
||||
return gopacket.InvalidEndpoint
|
||||
}
|
||||
|
||||
// NewMACEndpoint returns a new MAC address endpoint.
|
||||
func NewMACEndpoint(a net.HardwareAddr) gopacket.Endpoint {
|
||||
return gopacket.NewEndpoint(EndpointMAC, []byte(a))
|
||||
}
|
||||
func newPortEndpoint(t gopacket.EndpointType, p uint16) gopacket.Endpoint {
|
||||
return gopacket.NewEndpoint(t, []byte{byte(p >> 8), byte(p)})
|
||||
}
|
||||
|
||||
// NewTCPPortEndpoint returns an endpoint based on a TCP port.
|
||||
func NewTCPPortEndpoint(p TCPPort) gopacket.Endpoint {
|
||||
return newPortEndpoint(EndpointTCPPort, uint16(p))
|
||||
}
|
||||
|
||||
// NewUDPPortEndpoint returns an endpoint based on a UDP port.
|
||||
func NewUDPPortEndpoint(p UDPPort) gopacket.Endpoint {
|
||||
return newPortEndpoint(EndpointUDPPort, uint16(p))
|
||||
}
|
||||
|
||||
// NewSCTPPortEndpoint returns an endpoint based on a SCTP port.
|
||||
func NewSCTPPortEndpoint(p SCTPPort) gopacket.Endpoint {
|
||||
return newPortEndpoint(EndpointSCTPPort, uint16(p))
|
||||
}
|
||||
|
||||
// NewRUDPPortEndpoint returns an endpoint based on a RUDP port.
|
||||
func NewRUDPPortEndpoint(p RUDPPort) gopacket.Endpoint {
|
||||
return gopacket.NewEndpoint(EndpointRUDPPort, []byte{byte(p)})
|
||||
}
|
||||
|
||||
// NewUDPLitePortEndpoint returns an endpoint based on a UDPLite port.
|
||||
func NewUDPLitePortEndpoint(p UDPLitePort) gopacket.Endpoint {
|
||||
return newPortEndpoint(EndpointUDPLitePort, uint16(p))
|
||||
}
|
@ -0,0 +1,562 @@
|
||||
// Copyright 2012 Google, Inc. All rights reserved.
|
||||
// Copyright 2009-2011 Andreas Krennmair. 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 (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/google/gopacket"
|
||||
)
|
||||
|
||||
// EnumMetadata keeps track of a set of metadata for each enumeration value
|
||||
// for protocol enumerations.
|
||||
type EnumMetadata struct {
|
||||
// DecodeWith is the decoder to use to decode this protocol's data.
|
||||
DecodeWith gopacket.Decoder
|
||||
// Name is the name of the enumeration value.
|
||||
Name string
|
||||
// LayerType is the layer type implied by the given enum.
|
||||
LayerType gopacket.LayerType
|
||||
}
|
||||
|
||||
// errorFunc returns a decoder that spits out a specific error message.
|
||||
func errorFunc(msg string) gopacket.Decoder {
|
||||
var e = errors.New(msg)
|
||||
return gopacket.DecodeFunc(func([]byte, gopacket.PacketBuilder) error {
|
||||
return e
|
||||
})
|
||||
}
|
||||
|
||||
// EthernetType is an enumeration of ethernet type values, and acts as a decoder
|
||||
// for any type it supports.
|
||||
type EthernetType uint16
|
||||
|
||||
const (
|
||||
// EthernetTypeLLC is not an actual ethernet type. It is instead a
|
||||
// placeholder we use in Ethernet frames that use the 802.3 standard of
|
||||
// srcmac|dstmac|length|LLC instead of srcmac|dstmac|ethertype.
|
||||
EthernetTypeLLC EthernetType = 0
|
||||
EthernetTypeIPv4 EthernetType = 0x0800
|
||||
EthernetTypeARP EthernetType = 0x0806
|
||||
EthernetTypeIPv6 EthernetType = 0x86DD
|
||||
EthernetTypeCiscoDiscovery EthernetType = 0x2000
|
||||
EthernetTypeNortelDiscovery EthernetType = 0x01a2
|
||||
EthernetTypeTransparentEthernetBridging EthernetType = 0x6558
|
||||
EthernetTypeDot1Q EthernetType = 0x8100
|
||||
EthernetTypePPPoEDiscovery EthernetType = 0x8863
|
||||
EthernetTypePPPoESession EthernetType = 0x8864
|
||||
EthernetTypeMPLSUnicast EthernetType = 0x8847
|
||||
EthernetTypeMPLSMulticast EthernetType = 0x8848
|
||||
EthernetTypeEAPOL EthernetType = 0x888e
|
||||
EthernetTypeQinQ EthernetType = 0x88a8
|
||||
EthernetTypeLinkLayerDiscovery EthernetType = 0x88cc
|
||||
EthernetTypeEthernetCTP EthernetType = 0x9000
|
||||
)
|
||||
|
||||
// IPProtocol is an enumeration of IP protocol values, and acts as a decoder
|
||||
// for any type it supports.
|
||||
type IPProtocol uint8
|
||||
|
||||
const (
|
||||
IPProtocolIPv6HopByHop IPProtocol = 0
|
||||
IPProtocolICMPv4 IPProtocol = 1
|
||||
IPProtocolIGMP IPProtocol = 2
|
||||
IPProtocolIPv4 IPProtocol = 4
|
||||
IPProtocolTCP IPProtocol = 6
|
||||
IPProtocolUDP IPProtocol = 17
|
||||
IPProtocolRUDP IPProtocol = 27
|
||||
IPProtocolIPv6 IPProtocol = 41
|
||||
IPProtocolIPv6Routing IPProtocol = 43
|
||||
IPProtocolIPv6Fragment IPProtocol = 44
|
||||
IPProtocolGRE IPProtocol = 47
|
||||
IPProtocolESP IPProtocol = 50
|
||||
IPProtocolAH IPProtocol = 51
|
||||
IPProtocolICMPv6 IPProtocol = 58
|
||||
IPProtocolNoNextHeader IPProtocol = 59
|
||||
IPProtocolIPv6Destination IPProtocol = 60
|
||||
IPProtocolIPIP IPProtocol = 94
|
||||
IPProtocolEtherIP IPProtocol = 97
|
||||
IPProtocolVRRP IPProtocol = 112
|
||||
IPProtocolSCTP IPProtocol = 132
|
||||
IPProtocolUDPLite IPProtocol = 136
|
||||
IPProtocolMPLSInIP IPProtocol = 137
|
||||
)
|
||||
|
||||
// LinkType is an enumeration of link types, and acts as a decoder for any
|
||||
// link type it supports.
|
||||
type LinkType uint8
|
||||
|
||||
const (
|
||||
// According to pcap-linktype(7) and http://www.tcpdump.org/linktypes.html
|
||||
LinkTypeNull LinkType = 0
|
||||
LinkTypeEthernet LinkType = 1
|
||||
LinkTypeAX25 LinkType = 3
|
||||
LinkTypeTokenRing LinkType = 6
|
||||
LinkTypeArcNet LinkType = 7
|
||||
LinkTypeSLIP LinkType = 8
|
||||
LinkTypePPP LinkType = 9
|
||||
LinkTypeFDDI LinkType = 10
|
||||
LinkTypePPP_HDLC LinkType = 50
|
||||
LinkTypePPPEthernet LinkType = 51
|
||||
LinkTypeATM_RFC1483 LinkType = 100
|
||||
LinkTypeRaw LinkType = 101
|
||||
LinkTypeC_HDLC LinkType = 104
|
||||
LinkTypeIEEE802_11 LinkType = 105
|
||||
LinkTypeFRelay LinkType = 107
|
||||
LinkTypeLoop LinkType = 108
|
||||
LinkTypeLinuxSLL LinkType = 113
|
||||
LinkTypeLTalk LinkType = 114
|
||||
LinkTypePFLog LinkType = 117
|
||||
LinkTypePrismHeader LinkType = 119
|
||||
LinkTypeIPOverFC LinkType = 122
|
||||
LinkTypeSunATM LinkType = 123
|
||||
LinkTypeIEEE80211Radio LinkType = 127
|
||||
LinkTypeARCNetLinux LinkType = 129
|
||||
LinkTypeIPOver1394 LinkType = 138
|
||||
LinkTypeMTP2Phdr LinkType = 139
|
||||
LinkTypeMTP2 LinkType = 140
|
||||
LinkTypeMTP3 LinkType = 141
|
||||
LinkTypeSCCP LinkType = 142
|
||||
LinkTypeDOCSIS LinkType = 143
|
||||
LinkTypeLinuxIRDA LinkType = 144
|
||||
LinkTypeLinuxLAPD LinkType = 177
|
||||
LinkTypeLinuxUSB LinkType = 220
|
||||
LinkTypeIPv4 LinkType = 228
|
||||
LinkTypeIPv6 LinkType = 229
|
||||
)
|
||||
|
||||
// PPPoECode is the PPPoE code enum, taken from http://tools.ietf.org/html/rfc2516
|
||||
type PPPoECode uint8
|
||||
|
||||
const (
|
||||
PPPoECodePADI PPPoECode = 0x09
|
||||
PPPoECodePADO PPPoECode = 0x07
|
||||
PPPoECodePADR PPPoECode = 0x19
|
||||
PPPoECodePADS PPPoECode = 0x65
|
||||
PPPoECodePADT PPPoECode = 0xA7
|
||||
PPPoECodeSession PPPoECode = 0x00
|
||||
)
|
||||
|
||||
// PPPType is an enumeration of PPP type values, and acts as a decoder for any
|
||||
// type it supports.
|
||||
type PPPType uint16
|
||||
|
||||
const (
|
||||
PPPTypeIPv4 PPPType = 0x0021
|
||||
PPPTypeIPv6 PPPType = 0x0057
|
||||
PPPTypeMPLSUnicast PPPType = 0x0281
|
||||
PPPTypeMPLSMulticast PPPType = 0x0283
|
||||
)
|
||||
|
||||
// SCTPChunkType is an enumeration of chunk types inside SCTP packets.
|
||||
type SCTPChunkType uint8
|
||||
|
||||
const (
|
||||
SCTPChunkTypeData SCTPChunkType = 0
|
||||
SCTPChunkTypeInit SCTPChunkType = 1
|
||||
SCTPChunkTypeInitAck SCTPChunkType = 2
|
||||
SCTPChunkTypeSack SCTPChunkType = 3
|
||||
SCTPChunkTypeHeartbeat SCTPChunkType = 4
|
||||
SCTPChunkTypeHeartbeatAck SCTPChunkType = 5
|
||||
SCTPChunkTypeAbort SCTPChunkType = 6
|
||||
SCTPChunkTypeShutdown SCTPChunkType = 7
|
||||
SCTPChunkTypeShutdownAck SCTPChunkType = 8
|
||||
SCTPChunkTypeError SCTPChunkType = 9
|
||||
SCTPChunkTypeCookieEcho SCTPChunkType = 10
|
||||
SCTPChunkTypeCookieAck SCTPChunkType = 11
|
||||
SCTPChunkTypeShutdownComplete SCTPChunkType = 14
|
||||
)
|
||||
|
||||
// FDDIFrameControl is an enumeration of FDDI frame control bytes.
|
||||
type FDDIFrameControl uint8
|
||||
|
||||
const (
|
||||
FDDIFrameControlLLC FDDIFrameControl = 0x50
|
||||
)
|
||||
|
||||
// EAPOLType is an enumeration of EAPOL packet types.
|
||||
type EAPOLType uint8
|
||||
|
||||
const (
|
||||
EAPOLTypeEAP EAPOLType = 0
|
||||
EAPOLTypeStart EAPOLType = 1
|
||||
EAPOLTypeLogOff EAPOLType = 2
|
||||
EAPOLTypeKey EAPOLType = 3
|
||||
EAPOLTypeASFAlert EAPOLType = 4
|
||||
)
|
||||
|
||||
// ProtocolFamily is the set of values defined as PF_* in sys/socket.h
|
||||
type ProtocolFamily uint8
|
||||
|
||||
const (
|
||||
ProtocolFamilyIPv4 ProtocolFamily = 2
|
||||
// BSDs use different values for INET6... glory be. These values taken from
|
||||
// tcpdump 4.3.0.
|
||||
ProtocolFamilyIPv6BSD ProtocolFamily = 24
|
||||
ProtocolFamilyIPv6FreeBSD ProtocolFamily = 28
|
||||
ProtocolFamilyIPv6Darwin ProtocolFamily = 30
|
||||
ProtocolFamilyIPv6Linux ProtocolFamily = 10
|
||||
)
|
||||
|
||||
// Dot11Type is a combination of IEEE 802.11 frame's Type and Subtype fields.
|
||||
// By combining these two fields together into a single type, we're able to
|
||||
// provide a String function that correctly displays the subtype given the
|
||||
// top-level type.
|
||||
//
|
||||
// If you just care about the top-level type, use the MainType function.
|
||||
type Dot11Type uint8
|
||||
|
||||
// MainType strips the subtype information from the given type,
|
||||
// returning just the overarching type (Mgmt, Ctrl, Data, Reserved).
|
||||
func (d Dot11Type) MainType() Dot11Type {
|
||||
return d & dot11TypeMask
|
||||
}
|
||||
|
||||
const (
|
||||
Dot11TypeMgmt Dot11Type = 0x00
|
||||
Dot11TypeCtrl Dot11Type = 0x01
|
||||
Dot11TypeData Dot11Type = 0x02
|
||||
Dot11TypeReserved Dot11Type = 0x03
|
||||
dot11TypeMask = 0x03
|
||||
|
||||
// The following are type/subtype conglomerations.
|
||||
|
||||
// Management
|
||||
Dot11TypeMgmtAssociationReq Dot11Type = 0x00
|
||||
Dot11TypeMgmtAssociationResp Dot11Type = 0x04
|
||||
Dot11TypeMgmtReassociationReq Dot11Type = 0x08
|
||||
Dot11TypeMgmtReassociationResp Dot11Type = 0x0c
|
||||
Dot11TypeMgmtProbeReq Dot11Type = 0x10
|
||||
Dot11TypeMgmtProbeResp Dot11Type = 0x14
|
||||
Dot11TypeMgmtMeasurementPilot Dot11Type = 0x18
|
||||
Dot11TypeMgmtBeacon Dot11Type = 0x20
|
||||
Dot11TypeMgmtATIM Dot11Type = 0x24
|
||||
Dot11TypeMgmtDisassociation Dot11Type = 0x28
|
||||
Dot11TypeMgmtAuthentication Dot11Type = 0x2c
|
||||
Dot11TypeMgmtDeauthentication Dot11Type = 0x30
|
||||
Dot11TypeMgmtAction Dot11Type = 0x34
|
||||
Dot11TypeMgmtActionNoAck Dot11Type = 0x38
|
||||
|
||||
// Control
|
||||
Dot11TypeCtrlWrapper Dot11Type = 0x1d
|
||||
Dot11TypeCtrlBlockAckReq Dot11Type = 0x21
|
||||
Dot11TypeCtrlBlockAck Dot11Type = 0x25
|
||||
Dot11TypeCtrlPowersavePoll Dot11Type = 0x29
|
||||
Dot11TypeCtrlRTS Dot11Type = 0x2d
|
||||
Dot11TypeCtrlCTS Dot11Type = 0x31
|
||||
Dot11TypeCtrlAck Dot11Type = 0x35
|
||||
Dot11TypeCtrlCFEnd Dot11Type = 0x39
|
||||
Dot11TypeCtrlCFEndAck Dot11Type = 0x3d
|
||||
|
||||
// Data
|
||||
Dot11TypeDataCFAck Dot11Type = 0x06
|
||||
Dot11TypeDataCFPoll Dot11Type = 0x0a
|
||||
Dot11TypeDataCFAckPoll Dot11Type = 0x0e
|
||||
Dot11TypeDataNull Dot11Type = 0x12
|
||||
Dot11TypeDataCFAckNoData Dot11Type = 0x16
|
||||
Dot11TypeDataCFPollNoData Dot11Type = 0x1a
|
||||
Dot11TypeDataCFAckPollNoData Dot11Type = 0x1e
|
||||
Dot11TypeDataQOSData Dot11Type = 0x22
|
||||
Dot11TypeDataQOSDataCFAck Dot11Type = 0x26
|
||||
Dot11TypeDataQOSDataCFPoll Dot11Type = 0x2a
|
||||
Dot11TypeDataQOSDataCFAckPoll Dot11Type = 0x2e
|
||||
Dot11TypeDataQOSNull Dot11Type = 0x32
|
||||
Dot11TypeDataQOSCFPollNoData Dot11Type = 0x3a
|
||||
Dot11TypeDataQOSCFAckPollNoData Dot11Type = 0x3e
|
||||
)
|
||||
|
||||
var (
|
||||
// Each of the following arrays contains mappings of how to handle enum
|
||||
// values for various enum types in gopacket/layers.
|
||||
//
|
||||
// So, EthernetTypeMetadata[2] contains information on how to handle EthernetType
|
||||
// 2, including which name to give it and which decoder to use to decode
|
||||
// packet data of that type. These arrays are filled by default with all of the
|
||||
// protocols gopacket/layers knows how to handle, but users of the library can
|
||||
// add new decoders or override existing ones. For example, if you write a better
|
||||
// TCP decoder, you can override IPProtocolMetadata[IPProtocolTCP].DecodeWith
|
||||
// with your new decoder, and all gopacket/layers decoding will use your new
|
||||
// decoder whenever they encounter that IPProtocol.
|
||||
EthernetTypeMetadata [65536]EnumMetadata
|
||||
IPProtocolMetadata [256]EnumMetadata
|
||||
SCTPChunkTypeMetadata [256]EnumMetadata
|
||||
PPPTypeMetadata [65536]EnumMetadata
|
||||
PPPoECodeMetadata [256]EnumMetadata
|
||||
LinkTypeMetadata [256]EnumMetadata
|
||||
FDDIFrameControlMetadata [256]EnumMetadata
|
||||
EAPOLTypeMetadata [256]EnumMetadata
|
||||
ProtocolFamilyMetadata [256]EnumMetadata
|
||||
Dot11TypeMetadata [256]EnumMetadata
|
||||
USBTypeMetadata [256]EnumMetadata
|
||||
)
|
||||
|
||||
func (a EthernetType) Decode(data []byte, p gopacket.PacketBuilder) error {
|
||||
return EthernetTypeMetadata[a].DecodeWith.Decode(data, p)
|
||||
}
|
||||
func (a EthernetType) String() string {
|
||||
return EthernetTypeMetadata[a].Name
|
||||
}
|
||||
func (a EthernetType) LayerType() gopacket.LayerType {
|
||||
return EthernetTypeMetadata[a].LayerType
|
||||
}
|
||||
func (a IPProtocol) Decode(data []byte, p gopacket.PacketBuilder) error {
|
||||
return IPProtocolMetadata[a].DecodeWith.Decode(data, p)
|
||||
}
|
||||
func (a IPProtocol) String() string {
|
||||
return IPProtocolMetadata[a].Name
|
||||
}
|
||||
func (a IPProtocol) LayerType() gopacket.LayerType {
|
||||
return IPProtocolMetadata[a].LayerType
|
||||
}
|
||||
func (a SCTPChunkType) Decode(data []byte, p gopacket.PacketBuilder) error {
|
||||
return SCTPChunkTypeMetadata[a].DecodeWith.Decode(data, p)
|
||||
}
|
||||
func (a SCTPChunkType) String() string {
|
||||
return SCTPChunkTypeMetadata[a].Name
|
||||
}
|
||||
func (a PPPType) Decode(data []byte, p gopacket.PacketBuilder) error {
|
||||
return PPPTypeMetadata[a].DecodeWith.Decode(data, p)
|
||||
}
|
||||
func (a PPPType) String() string {
|
||||
return PPPTypeMetadata[a].Name
|
||||
}
|
||||
func (a LinkType) Decode(data []byte, p gopacket.PacketBuilder) error {
|
||||
return LinkTypeMetadata[a].DecodeWith.Decode(data, p)
|
||||
}
|
||||
func (a LinkType) String() string {
|
||||
return LinkTypeMetadata[a].Name
|
||||
}
|
||||
func (a PPPoECode) Decode(data []byte, p gopacket.PacketBuilder) error {
|
||||
return PPPoECodeMetadata[a].DecodeWith.Decode(data, p)
|
||||
}
|
||||
func (a PPPoECode) String() string {
|
||||
return PPPoECodeMetadata[a].Name
|
||||
}
|
||||
func (a FDDIFrameControl) Decode(data []byte, p gopacket.PacketBuilder) error {
|
||||
return FDDIFrameControlMetadata[a].DecodeWith.Decode(data, p)
|
||||
}
|
||||
func (a FDDIFrameControl) String() string {
|
||||
return FDDIFrameControlMetadata[a].Name
|
||||
}
|
||||
func (a EAPOLType) Decode(data []byte, p gopacket.PacketBuilder) error {
|
||||
return EAPOLTypeMetadata[a].DecodeWith.Decode(data, p)
|
||||
}
|
||||
func (a EAPOLType) String() string {
|
||||
return EAPOLTypeMetadata[a].Name
|
||||
}
|
||||
func (a EAPOLType) LayerType() gopacket.LayerType {
|
||||
return EAPOLTypeMetadata[a].LayerType
|
||||
}
|
||||
func (a ProtocolFamily) Decode(data []byte, p gopacket.PacketBuilder) error {
|
||||
return ProtocolFamilyMetadata[a].DecodeWith.Decode(data, p)
|
||||
}
|
||||
func (a ProtocolFamily) String() string {
|
||||
return ProtocolFamilyMetadata[a].Name
|
||||
}
|
||||
func (a ProtocolFamily) LayerType() gopacket.LayerType {
|
||||
return ProtocolFamilyMetadata[a].LayerType
|
||||
}
|
||||
func (a Dot11Type) Decode(data []byte, p gopacket.PacketBuilder) error {
|
||||
return Dot11TypeMetadata[a].DecodeWith.Decode(data, p)
|
||||
}
|
||||
func (a Dot11Type) String() string {
|
||||
return Dot11TypeMetadata[a].Name
|
||||
}
|
||||
func (a Dot11Type) LayerType() gopacket.LayerType {
|
||||
return Dot11TypeMetadata[a].LayerType
|
||||
}
|
||||
|
||||
// Decode a raw v4 or v6 IP packet.
|
||||
func decodeIPv4or6(data []byte, p gopacket.PacketBuilder) error {
|
||||
version := data[0] >> 4
|
||||
switch version {
|
||||
case 4:
|
||||
return decodeIPv4(data, p)
|
||||
case 6:
|
||||
return decodeIPv6(data, p)
|
||||
}
|
||||
return fmt.Errorf("Invalid IP packet version %v", version)
|
||||
}
|
||||
|
||||
func init() {
|
||||
// Here we link up all enumerations with their respective names and decoders.
|
||||
for i := 0; i < 65536; i++ {
|
||||
EthernetTypeMetadata[i] = EnumMetadata{
|
||||
DecodeWith: errorFunc(fmt.Sprintf("Unable to decode ethernet type %d", i)),
|
||||
Name: fmt.Sprintf("UnknownEthernetType(%d)", i),
|
||||
}
|
||||
PPPTypeMetadata[i] = EnumMetadata{
|
||||
DecodeWith: errorFunc(fmt.Sprintf("Unable to decode PPP type %d", i)),
|
||||
Name: fmt.Sprintf("UnknownPPPType(%d)", i),
|
||||
}
|
||||
}
|
||||
for i := 0; i < 256; i++ {
|
||||
IPProtocolMetadata[i] = EnumMetadata{
|
||||
DecodeWith: errorFunc(fmt.Sprintf("Unable to decode IP protocol %d", i)),
|
||||
Name: fmt.Sprintf("UnknownIPProtocol(%d)", i),
|
||||
}
|
||||
SCTPChunkTypeMetadata[i] = EnumMetadata{
|
||||
DecodeWith: errorFunc(fmt.Sprintf("Unable to decode SCTP chunk type %d", i)),
|
||||
Name: fmt.Sprintf("UnknownSCTPChunkType(%d)", i),
|
||||
}
|
||||
PPPoECodeMetadata[i] = EnumMetadata{
|
||||
DecodeWith: errorFunc(fmt.Sprintf("Unable to decode PPPoE code %d", i)),
|
||||
Name: fmt.Sprintf("UnknownPPPoECode(%d)", i),
|
||||
}
|
||||
LinkTypeMetadata[i] = EnumMetadata{
|
||||
DecodeWith: errorFunc(fmt.Sprintf("Unable to decode link type %d", i)),
|
||||
Name: fmt.Sprintf("UnknownLinkType(%d)", i),
|
||||
}
|
||||
FDDIFrameControlMetadata[i] = EnumMetadata{
|
||||
DecodeWith: errorFunc(fmt.Sprintf("Unable to decode FDDI frame control %d", i)),
|
||||
Name: fmt.Sprintf("UnknownFDDIFrameControl(%d)", i),
|
||||
}
|
||||
EAPOLTypeMetadata[i] = EnumMetadata{
|
||||
DecodeWith: errorFunc(fmt.Sprintf("Unable to decode EAPOL type %d", i)),
|
||||
Name: fmt.Sprintf("UnknownEAPOLType(%d)", i),
|
||||
}
|
||||
ProtocolFamilyMetadata[i] = EnumMetadata{
|
||||
DecodeWith: errorFunc(fmt.Sprintf("Unable to decode protocol family %d", i)),
|
||||
Name: fmt.Sprintf("UnknownProtocolFamily(%d)", i),
|
||||
}
|
||||
Dot11TypeMetadata[i] = EnumMetadata{
|
||||
DecodeWith: errorFunc(fmt.Sprintf("Unable to decode Dot11 type %d", i)),
|
||||
Name: fmt.Sprintf("UnknownDot11Type(%d)", i),
|
||||
}
|
||||
}
|
||||
|
||||
EthernetTypeMetadata[EthernetTypeLLC] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeLLC), Name: "LLC", LayerType: LayerTypeLLC}
|
||||
EthernetTypeMetadata[EthernetTypeIPv4] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeIPv4), Name: "IPv4", LayerType: LayerTypeIPv4}
|
||||
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[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}
|
||||
EthernetTypeMetadata[EthernetTypeCiscoDiscovery] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeCiscoDiscovery), Name: "CiscoDiscovery", LayerType: LayerTypeCiscoDiscovery}
|
||||
EthernetTypeMetadata[EthernetTypeNortelDiscovery] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeNortelDiscovery), Name: "NortelDiscovery", LayerType: LayerTypeNortelDiscovery}
|
||||
EthernetTypeMetadata[EthernetTypeLinkLayerDiscovery] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeLinkLayerDiscovery), Name: "LinkLayerDiscovery", LayerType: LayerTypeLinkLayerDiscovery}
|
||||
EthernetTypeMetadata[EthernetTypeMPLSUnicast] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeMPLS), Name: "MPLSUnicast", LayerType: LayerTypeMPLS}
|
||||
EthernetTypeMetadata[EthernetTypeMPLSMulticast] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeMPLS), Name: "MPLSMulticast", LayerType: LayerTypeMPLS}
|
||||
EthernetTypeMetadata[EthernetTypeEAPOL] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeEAPOL), Name: "EAPOL", LayerType: LayerTypeEAPOL}
|
||||
EthernetTypeMetadata[EthernetTypeQinQ] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot1Q), Name: "Dot1Q", LayerType: LayerTypeDot1Q}
|
||||
EthernetTypeMetadata[EthernetTypeTransparentEthernetBridging] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeEthernet), Name: "TransparentEthernetBridging", LayerType: LayerTypeEthernet}
|
||||
|
||||
IPProtocolMetadata[IPProtocolIPv4] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeIPv4), Name: "IPv4", LayerType: LayerTypeIPv4}
|
||||
IPProtocolMetadata[IPProtocolTCP] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeTCP), Name: "TCP", LayerType: LayerTypeTCP}
|
||||
IPProtocolMetadata[IPProtocolUDP] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeUDP), Name: "UDP", LayerType: LayerTypeUDP}
|
||||
IPProtocolMetadata[IPProtocolICMPv4] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeICMPv4), Name: "ICMPv4", LayerType: LayerTypeICMPv4}
|
||||
IPProtocolMetadata[IPProtocolICMPv6] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeICMPv6), Name: "ICMPv6", LayerType: LayerTypeICMPv6}
|
||||
IPProtocolMetadata[IPProtocolSCTP] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeSCTP), Name: "SCTP", LayerType: LayerTypeSCTP}
|
||||
IPProtocolMetadata[IPProtocolIPv6] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeIPv6), Name: "IPv6", LayerType: LayerTypeIPv6}
|
||||
IPProtocolMetadata[IPProtocolIPIP] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeIPv4), Name: "IPv4", LayerType: LayerTypeIPv4}
|
||||
IPProtocolMetadata[IPProtocolEtherIP] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeEtherIP), Name: "EtherIP", LayerType: LayerTypeEtherIP}
|
||||
IPProtocolMetadata[IPProtocolRUDP] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeRUDP), Name: "RUDP", LayerType: LayerTypeRUDP}
|
||||
IPProtocolMetadata[IPProtocolGRE] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeGRE), Name: "GRE", LayerType: LayerTypeGRE}
|
||||
IPProtocolMetadata[IPProtocolIPv6HopByHop] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeIPv6HopByHop), Name: "IPv6HopByHop", LayerType: LayerTypeIPv6HopByHop}
|
||||
IPProtocolMetadata[IPProtocolIPv6Routing] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeIPv6Routing), Name: "IPv6Routing", LayerType: LayerTypeIPv6Routing}
|
||||
IPProtocolMetadata[IPProtocolIPv6Fragment] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeIPv6Fragment), Name: "IPv6Fragment", LayerType: LayerTypeIPv6Fragment}
|
||||
IPProtocolMetadata[IPProtocolIPv6Destination] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeIPv6Destination), Name: "IPv6Destination", LayerType: LayerTypeIPv6Destination}
|
||||
IPProtocolMetadata[IPProtocolAH] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeIPSecAH), Name: "IPSecAH", LayerType: LayerTypeIPSecAH}
|
||||
IPProtocolMetadata[IPProtocolESP] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeIPSecESP), Name: "IPSecESP", LayerType: LayerTypeIPSecESP}
|
||||
IPProtocolMetadata[IPProtocolUDPLite] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeUDPLite), Name: "UDPLite", LayerType: LayerTypeUDPLite}
|
||||
IPProtocolMetadata[IPProtocolMPLSInIP] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeMPLS), Name: "MPLS", LayerType: LayerTypeMPLS}
|
||||
IPProtocolMetadata[IPProtocolNoNextHeader] = EnumMetadata{DecodeWith: gopacket.DecodePayload, Name: "NoNextHeader", LayerType: gopacket.LayerTypePayload}
|
||||
IPProtocolMetadata[IPProtocolIGMP] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeIGMP), Name: "IGMP", LayerType: LayerTypeIGMP}
|
||||
IPProtocolMetadata[IPProtocolVRRP] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeVRRP), Name: "VRRP", LayerType: LayerTypeVRRP}
|
||||
|
||||
SCTPChunkTypeMetadata[SCTPChunkTypeData] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeSCTPData), Name: "Data"}
|
||||
SCTPChunkTypeMetadata[SCTPChunkTypeInit] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeSCTPInit), Name: "Init"}
|
||||
SCTPChunkTypeMetadata[SCTPChunkTypeInitAck] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeSCTPInit), Name: "InitAck"}
|
||||
SCTPChunkTypeMetadata[SCTPChunkTypeSack] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeSCTPSack), Name: "Sack"}
|
||||
SCTPChunkTypeMetadata[SCTPChunkTypeHeartbeat] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeSCTPHeartbeat), Name: "Heartbeat"}
|
||||
SCTPChunkTypeMetadata[SCTPChunkTypeHeartbeatAck] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeSCTPHeartbeat), Name: "HeartbeatAck"}
|
||||
SCTPChunkTypeMetadata[SCTPChunkTypeAbort] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeSCTPError), Name: "Abort"}
|
||||
SCTPChunkTypeMetadata[SCTPChunkTypeError] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeSCTPError), Name: "Error"}
|
||||
SCTPChunkTypeMetadata[SCTPChunkTypeShutdown] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeSCTPShutdown), Name: "Shutdown"}
|
||||
SCTPChunkTypeMetadata[SCTPChunkTypeShutdownAck] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeSCTPShutdownAck), Name: "ShutdownAck"}
|
||||
SCTPChunkTypeMetadata[SCTPChunkTypeCookieEcho] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeSCTPCookieEcho), Name: "CookieEcho"}
|
||||
SCTPChunkTypeMetadata[SCTPChunkTypeCookieAck] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeSCTPEmptyLayer), Name: "CookieAck"}
|
||||
SCTPChunkTypeMetadata[SCTPChunkTypeShutdownComplete] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeSCTPEmptyLayer), Name: "ShutdownComplete"}
|
||||
|
||||
PPPTypeMetadata[PPPTypeIPv4] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeIPv4), Name: "IPv4"}
|
||||
PPPTypeMetadata[PPPTypeIPv6] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeIPv6), Name: "IPv6"}
|
||||
PPPTypeMetadata[PPPTypeMPLSUnicast] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeMPLS), Name: "MPLSUnicast"}
|
||||
PPPTypeMetadata[PPPTypeMPLSMulticast] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeMPLS), Name: "MPLSMulticast"}
|
||||
|
||||
PPPoECodeMetadata[PPPoECodeSession] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodePPP), Name: "PPP"}
|
||||
|
||||
LinkTypeMetadata[LinkTypeEthernet] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeEthernet), Name: "Ethernet"}
|
||||
LinkTypeMetadata[LinkTypePPP] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodePPP), Name: "PPP"}
|
||||
LinkTypeMetadata[LinkTypeFDDI] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeFDDI), Name: "FDDI"}
|
||||
LinkTypeMetadata[LinkTypeNull] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeLoopback), Name: "Null"}
|
||||
LinkTypeMetadata[LinkTypeIEEE802_11] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11), Name: "Dot11"}
|
||||
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"}
|
||||
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"}
|
||||
LinkTypeMetadata[LinkTypeLinuxSLL] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeLinuxSLL), Name: "Linux SLL"}
|
||||
LinkTypeMetadata[LinkTypePrismHeader] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodePrismHeader), Name: "Prism"}
|
||||
|
||||
FDDIFrameControlMetadata[FDDIFrameControlLLC] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeLLC), Name: "LLC"}
|
||||
|
||||
EAPOLTypeMetadata[EAPOLTypeEAP] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeEAP), Name: "EAP", LayerType: LayerTypeEAP}
|
||||
|
||||
ProtocolFamilyMetadata[ProtocolFamilyIPv4] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeIPv4), Name: "IPv4", LayerType: LayerTypeIPv4}
|
||||
ProtocolFamilyMetadata[ProtocolFamilyIPv6BSD] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeIPv6), Name: "IPv6", LayerType: LayerTypeIPv6}
|
||||
ProtocolFamilyMetadata[ProtocolFamilyIPv6FreeBSD] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeIPv6), Name: "IPv6", LayerType: LayerTypeIPv6}
|
||||
ProtocolFamilyMetadata[ProtocolFamilyIPv6Darwin] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeIPv6), Name: "IPv6", LayerType: LayerTypeIPv6}
|
||||
ProtocolFamilyMetadata[ProtocolFamilyIPv6Linux] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeIPv6), Name: "IPv6", LayerType: LayerTypeIPv6}
|
||||
|
||||
Dot11TypeMetadata[Dot11TypeMgmtAssociationReq] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11MgmtAssociationReq), Name: "MgmtAssociationReq", LayerType: LayerTypeDot11MgmtAssociationReq}
|
||||
Dot11TypeMetadata[Dot11TypeMgmtAssociationResp] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11MgmtAssociationResp), Name: "MgmtAssociationResp", LayerType: LayerTypeDot11MgmtAssociationResp}
|
||||
Dot11TypeMetadata[Dot11TypeMgmtReassociationReq] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11MgmtReassociationReq), Name: "MgmtReassociationReq", LayerType: LayerTypeDot11MgmtReassociationReq}
|
||||
Dot11TypeMetadata[Dot11TypeMgmtReassociationResp] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11MgmtReassociationResp), Name: "MgmtReassociationResp", LayerType: LayerTypeDot11MgmtReassociationResp}
|
||||
Dot11TypeMetadata[Dot11TypeMgmtProbeReq] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11MgmtProbeReq), Name: "MgmtProbeReq", LayerType: LayerTypeDot11MgmtProbeReq}
|
||||
Dot11TypeMetadata[Dot11TypeMgmtProbeResp] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11MgmtProbeResp), Name: "MgmtProbeResp", LayerType: LayerTypeDot11MgmtProbeResp}
|
||||
Dot11TypeMetadata[Dot11TypeMgmtMeasurementPilot] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11MgmtMeasurementPilot), Name: "MgmtMeasurementPilot", LayerType: LayerTypeDot11MgmtMeasurementPilot}
|
||||
Dot11TypeMetadata[Dot11TypeMgmtBeacon] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11MgmtBeacon), Name: "MgmtBeacon", LayerType: LayerTypeDot11MgmtBeacon}
|
||||
Dot11TypeMetadata[Dot11TypeMgmtATIM] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11MgmtATIM), Name: "MgmtATIM", LayerType: LayerTypeDot11MgmtATIM}
|
||||
Dot11TypeMetadata[Dot11TypeMgmtDisassociation] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11MgmtDisassociation), Name: "MgmtDisassociation", LayerType: LayerTypeDot11MgmtDisassociation}
|
||||
Dot11TypeMetadata[Dot11TypeMgmtAuthentication] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11MgmtAuthentication), Name: "MgmtAuthentication", LayerType: LayerTypeDot11MgmtAuthentication}
|
||||
Dot11TypeMetadata[Dot11TypeMgmtDeauthentication] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11MgmtDeauthentication), Name: "MgmtDeauthentication", LayerType: LayerTypeDot11MgmtDeauthentication}
|
||||
Dot11TypeMetadata[Dot11TypeMgmtAction] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11MgmtAction), Name: "MgmtAction", LayerType: LayerTypeDot11MgmtAction}
|
||||
Dot11TypeMetadata[Dot11TypeMgmtActionNoAck] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11MgmtActionNoAck), Name: "MgmtActionNoAck", LayerType: LayerTypeDot11MgmtActionNoAck}
|
||||
Dot11TypeMetadata[Dot11TypeCtrl] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11Ctrl), Name: "Ctrl", LayerType: LayerTypeDot11Ctrl}
|
||||
Dot11TypeMetadata[Dot11TypeCtrlWrapper] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11Ctrl), Name: "CtrlWrapper", LayerType: LayerTypeDot11Ctrl}
|
||||
Dot11TypeMetadata[Dot11TypeCtrlBlockAckReq] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11CtrlBlockAckReq), Name: "CtrlBlockAckReq", LayerType: LayerTypeDot11CtrlBlockAckReq}
|
||||
Dot11TypeMetadata[Dot11TypeCtrlBlockAck] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11CtrlBlockAck), Name: "CtrlBlockAck", LayerType: LayerTypeDot11CtrlBlockAck}
|
||||
Dot11TypeMetadata[Dot11TypeCtrlPowersavePoll] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11CtrlPowersavePoll), Name: "CtrlPowersavePoll", LayerType: LayerTypeDot11CtrlPowersavePoll}
|
||||
Dot11TypeMetadata[Dot11TypeCtrlRTS] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11CtrlRTS), Name: "CtrlRTS", LayerType: LayerTypeDot11CtrlRTS}
|
||||
Dot11TypeMetadata[Dot11TypeCtrlCTS] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11CtrlCTS), Name: "CtrlCTS", LayerType: LayerTypeDot11CtrlCTS}
|
||||
Dot11TypeMetadata[Dot11TypeCtrlAck] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11CtrlAck), Name: "CtrlAck", LayerType: LayerTypeDot11CtrlAck}
|
||||
Dot11TypeMetadata[Dot11TypeCtrlCFEnd] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11CtrlCFEnd), Name: "CtrlCFEnd", LayerType: LayerTypeDot11CtrlCFEnd}
|
||||
Dot11TypeMetadata[Dot11TypeCtrlCFEndAck] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11CtrlCFEndAck), Name: "CtrlCFEndAck", LayerType: LayerTypeDot11CtrlCFEndAck}
|
||||
Dot11TypeMetadata[Dot11TypeData] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11Data), Name: "Data", LayerType: LayerTypeDot11Data}
|
||||
Dot11TypeMetadata[Dot11TypeDataCFAck] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11DataCFAck), Name: "DataCFAck", LayerType: LayerTypeDot11DataCFAck}
|
||||
Dot11TypeMetadata[Dot11TypeDataCFPoll] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11DataCFPoll), Name: "DataCFPoll", LayerType: LayerTypeDot11DataCFPoll}
|
||||
Dot11TypeMetadata[Dot11TypeDataCFAckPoll] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11DataCFAckPoll), Name: "DataCFAckPoll", LayerType: LayerTypeDot11DataCFAckPoll}
|
||||
Dot11TypeMetadata[Dot11TypeDataNull] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11DataNull), Name: "DataNull", LayerType: LayerTypeDot11DataNull}
|
||||
Dot11TypeMetadata[Dot11TypeDataCFAckNoData] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11DataCFAckNoData), Name: "DataCFAckNoData", LayerType: LayerTypeDot11DataCFAckNoData}
|
||||
Dot11TypeMetadata[Dot11TypeDataCFPollNoData] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11DataCFPollNoData), Name: "DataCFPollNoData", LayerType: LayerTypeDot11DataCFPollNoData}
|
||||
Dot11TypeMetadata[Dot11TypeDataCFAckPollNoData] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11DataCFAckPollNoData), Name: "DataCFAckPollNoData", LayerType: LayerTypeDot11DataCFAckPollNoData}
|
||||
Dot11TypeMetadata[Dot11TypeDataQOSData] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11DataQOSData), Name: "DataQOSData", LayerType: LayerTypeDot11DataQOSData}
|
||||
Dot11TypeMetadata[Dot11TypeDataQOSDataCFAck] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11DataQOSDataCFAck), Name: "DataQOSDataCFAck", LayerType: LayerTypeDot11DataQOSDataCFAck}
|
||||
Dot11TypeMetadata[Dot11TypeDataQOSDataCFPoll] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11DataQOSDataCFPoll), Name: "DataQOSDataCFPoll", LayerType: LayerTypeDot11DataQOSDataCFPoll}
|
||||
Dot11TypeMetadata[Dot11TypeDataQOSDataCFAckPoll] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11DataQOSDataCFAckPoll), Name: "DataQOSDataCFAckPoll", LayerType: LayerTypeDot11DataQOSDataCFAckPoll}
|
||||
Dot11TypeMetadata[Dot11TypeDataQOSNull] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11DataQOSNull), Name: "DataQOSNull", LayerType: LayerTypeDot11DataQOSNull}
|
||||
Dot11TypeMetadata[Dot11TypeDataQOSCFPollNoData] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11DataQOSCFPollNoData), Name: "DataQOSCFPollNoData", LayerType: LayerTypeDot11DataQOSCFPollNoData}
|
||||
Dot11TypeMetadata[Dot11TypeDataQOSCFAckPollNoData] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11DataQOSCFAckPollNoData), Name: "DataQOSCFAckPollNoData", LayerType: LayerTypeDot11DataQOSCFAckPollNoData}
|
||||
|
||||
USBTypeMetadata[USBTransportTypeInterrupt] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeUSBInterrupt), Name: "Interrupt", LayerType: LayerTypeUSBInterrupt}
|
||||
USBTypeMetadata[USBTransportTypeControl] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeUSBControl), Name: "Control", LayerType: LayerTypeUSBControl}
|
||||
USBTypeMetadata[USBTransportTypeBulk] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeUSBBulk), Name: "Bulk", LayerType: LayerTypeUSBBulk}
|
||||
}
|
@ -0,0 +1,45 @@
|
||||
// Copyright 2012 Google, Inc. All rights reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the LICENSE file in the root of the source
|
||||
// tree.
|
||||
|
||||
package layers
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"github.com/google/gopacket"
|
||||
)
|
||||
|
||||
// EtherIP is the struct for storing RFC 3378 EtherIP packet headers.
|
||||
type EtherIP struct {
|
||||
BaseLayer
|
||||
Version uint8
|
||||
Reserved uint16
|
||||
}
|
||||
|
||||
// LayerType returns gopacket.LayerTypeEtherIP.
|
||||
func (e *EtherIP) LayerType() gopacket.LayerType { return LayerTypeEtherIP }
|
||||
|
||||
// DecodeFromBytes decodes the given bytes into this layer.
|
||||
func (e *EtherIP) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
|
||||
e.Version = data[0] >> 4
|
||||
e.Reserved = binary.BigEndian.Uint16(data[:2]) & 0x0fff
|
||||
e.BaseLayer = BaseLayer{data[:2], data[2:]}
|
||||
return nil
|
||||
}
|
||||
|
||||
// CanDecode returns the set of layer types that this DecodingLayer can decode.
|
||||
func (e *EtherIP) CanDecode() gopacket.LayerClass {
|
||||
return LayerTypeEtherIP
|
||||
}
|
||||
|
||||
// NextLayerType returns the layer type contained by this DecodingLayer.
|
||||
func (e *EtherIP) NextLayerType() gopacket.LayerType {
|
||||
return LayerTypeEthernet
|
||||
}
|
||||
|
||||
func decodeEtherIP(data []byte, p gopacket.PacketBuilder) error {
|
||||
e := &EtherIP{}
|
||||
return decodingLayerDecoder(e, data, p)
|
||||
}
|
@ -0,0 +1,122 @@
|
||||
// Copyright 2012 Google, Inc. All rights reserved.
|
||||
// Copyright 2009-2011 Andreas Krennmair. 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"
|
||||
"fmt"
|
||||
"github.com/google/gopacket"
|
||||
"net"
|
||||
)
|
||||
|
||||
// EthernetBroadcast is the broadcast MAC address used by Ethernet.
|
||||
var EthernetBroadcast = net.HardwareAddr{0xff, 0xff, 0xff, 0xff, 0xff, 0xff}
|
||||
|
||||
// Ethernet is the layer for Ethernet frame headers.
|
||||
type Ethernet struct {
|
||||
BaseLayer
|
||||
SrcMAC, DstMAC net.HardwareAddr
|
||||
EthernetType EthernetType
|
||||
// Length is only set if a length field exists within this header. Ethernet
|
||||
// headers follow two different standards, one that uses an EthernetType, the
|
||||
// other which defines a length the follows with a LLC header (802.3). If the
|
||||
// former is the case, we set EthernetType and Length stays 0. In the latter
|
||||
// case, we set Length and EthernetType = EthernetTypeLLC.
|
||||
Length uint16
|
||||
}
|
||||
|
||||
// LayerType returns LayerTypeEthernet
|
||||
func (e *Ethernet) LayerType() gopacket.LayerType { return LayerTypeEthernet }
|
||||
|
||||
func (e *Ethernet) LinkFlow() gopacket.Flow {
|
||||
return gopacket.NewFlow(EndpointMAC, e.SrcMAC, e.DstMAC)
|
||||
}
|
||||
|
||||
func (eth *Ethernet) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
|
||||
if len(data) < 14 {
|
||||
return errors.New("Ethernet packet too small")
|
||||
}
|
||||
eth.DstMAC = net.HardwareAddr(data[0:6])
|
||||
eth.SrcMAC = net.HardwareAddr(data[6:12])
|
||||
eth.EthernetType = EthernetType(binary.BigEndian.Uint16(data[12:14]))
|
||||
eth.BaseLayer = BaseLayer{data[:14], data[14:]}
|
||||
if eth.EthernetType < 0x0600 {
|
||||
eth.Length = uint16(eth.EthernetType)
|
||||
eth.EthernetType = EthernetTypeLLC
|
||||
if cmp := len(eth.Payload) - int(eth.Length); cmp < 0 {
|
||||
df.SetTruncated()
|
||||
} else if cmp > 0 {
|
||||
// Strip off bytes at the end, since we have too many bytes
|
||||
eth.Payload = eth.Payload[:len(eth.Payload)-cmp]
|
||||
}
|
||||
// fmt.Println(eth)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// SerializeTo writes the serialized form of this layer into the
|
||||
// SerializationBuffer, implementing gopacket.SerializableLayer.
|
||||
// See the docs for gopacket.SerializableLayer for more info.
|
||||
func (eth *Ethernet) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
|
||||
if len(eth.DstMAC) != 6 {
|
||||
return fmt.Errorf("invalid dst MAC: %v", eth.DstMAC)
|
||||
}
|
||||
if len(eth.SrcMAC) != 6 {
|
||||
return fmt.Errorf("invalid src MAC: %v", eth.SrcMAC)
|
||||
}
|
||||
payload := b.Bytes()
|
||||
bytes, err := b.PrependBytes(14)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
copy(bytes, eth.DstMAC)
|
||||
copy(bytes[6:], eth.SrcMAC)
|
||||
if eth.Length != 0 || eth.EthernetType == EthernetTypeLLC {
|
||||
if opts.FixLengths {
|
||||
eth.Length = uint16(len(payload))
|
||||
}
|
||||
if eth.EthernetType != EthernetTypeLLC {
|
||||
return fmt.Errorf("ethernet type %v not compatible with length value %v", eth.EthernetType, eth.Length)
|
||||
} else if eth.Length > 0x0600 {
|
||||
return fmt.Errorf("invalid ethernet length %v", eth.Length)
|
||||
}
|
||||
binary.BigEndian.PutUint16(bytes[12:], eth.Length)
|
||||
} else {
|
||||
binary.BigEndian.PutUint16(bytes[12:], uint16(eth.EthernetType))
|
||||
}
|
||||
length := len(b.Bytes())
|
||||
if length < 60 {
|
||||
// Pad out to 60 bytes.
|
||||
padding, err := b.AppendBytes(60 - length)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
copy(padding, lotsOfZeros[:])
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (eth *Ethernet) CanDecode() gopacket.LayerClass {
|
||||
return LayerTypeEthernet
|
||||
}
|
||||
|
||||
func (eth *Ethernet) NextLayerType() gopacket.LayerType {
|
||||
return eth.EthernetType.LayerType()
|
||||
}
|
||||
|
||||
func decodeEthernet(data []byte, p gopacket.PacketBuilder) error {
|
||||
eth := &Ethernet{}
|
||||
err := eth.DecodeFromBytes(data, p)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
p.AddLayer(eth)
|
||||
p.SetLinkLayer(eth)
|
||||
return p.NextDecoder(eth.EthernetType)
|
||||
}
|
@ -0,0 +1,41 @@
|
||||
// Copyright 2012 Google, Inc. All rights reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the LICENSE file in the root of the source
|
||||
// tree.
|
||||
|
||||
package layers
|
||||
|
||||
import (
|
||||
"github.com/google/gopacket"
|
||||
"net"
|
||||
)
|
||||
|
||||
// FDDI contains the header for FDDI frames.
|
||||
type FDDI struct {
|
||||
BaseLayer
|
||||
FrameControl FDDIFrameControl
|
||||
Priority uint8
|
||||
SrcMAC, DstMAC net.HardwareAddr
|
||||
}
|
||||
|
||||
// LayerType returns LayerTypeFDDI.
|
||||
func (f *FDDI) LayerType() gopacket.LayerType { return LayerTypeFDDI }
|
||||
|
||||
// LinkFlow returns a new flow of type EndpointMAC.
|
||||
func (f *FDDI) LinkFlow() gopacket.Flow {
|
||||
return gopacket.NewFlow(EndpointMAC, f.SrcMAC, f.DstMAC)
|
||||
}
|
||||
|
||||
func decodeFDDI(data []byte, p gopacket.PacketBuilder) error {
|
||||
f := &FDDI{
|
||||
FrameControl: FDDIFrameControl(data[0] & 0xF8),
|
||||
Priority: data[0] & 0x07,
|
||||
SrcMAC: net.HardwareAddr(data[1:7]),
|
||||
DstMAC: net.HardwareAddr(data[7:13]),
|
||||
BaseLayer: BaseLayer{data[:13], data[13:]},
|
||||
}
|
||||
p.SetLinkLayer(f)
|
||||
p.AddLayer(f)
|
||||
return p.NextDecoder(f.FrameControl)
|
||||
}
|
@ -0,0 +1,109 @@
|
||||
// Copyright 2012 Google, Inc. All rights reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the LICENSE file in the root of the source
|
||||
// tree.
|
||||
|
||||
// +build ignore
|
||||
|
||||
// This binary pulls known ports from IANA, and uses them to populate
|
||||
// iana_ports.go's TCPPortNames and UDPPortNames maps.
|
||||
//
|
||||
// go run gen.go | gofmt > iana_ports.go
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/xml"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
const fmtString = `// Copyright 2012 Google, Inc. All rights reserved.
|
||||
|
||||
package layers
|
||||
|
||||
// Created by gen.go, don't edit manually
|
||||
// Generated at %s
|
||||
// Fetched from %q
|
||||
|
||||
// TCPPortNames contains the port names for all TCP ports.
|
||||
var TCPPortNames = tcpPortNames
|
||||
|
||||
// UDPPortNames contains the port names for all UDP ports.
|
||||
var UDPPortNames = udpPortNames
|
||||
|
||||
// SCTPPortNames contains the port names for all SCTP ports.
|
||||
var SCTPPortNames = sctpPortNames
|
||||
|
||||
var tcpPortNames = map[TCPPort]string{
|
||||
%s}
|
||||
var udpPortNames = map[UDPPort]string{
|
||||
%s}
|
||||
var sctpPortNames = map[SCTPPort]string{
|
||||
%s}
|
||||
`
|
||||
|
||||
var url = flag.String("url", "http://www.iana.org/assignments/service-names-port-numbers/service-names-port-numbers.xml", "URL to grab port numbers from")
|
||||
|
||||
func main() {
|
||||
fmt.Fprintf(os.Stderr, "Fetching ports from %q\n", *url)
|
||||
resp, err := http.Get(*url)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fmt.Fprintln(os.Stderr, "Parsing XML")
|
||||
var registry struct {
|
||||
Records []struct {
|
||||
Protocol string `xml:"protocol"`
|
||||
Number string `xml:"number"`
|
||||
Name string `xml:"name"`
|
||||
} `xml:"record"`
|
||||
}
|
||||
xml.Unmarshal(body, ®istry)
|
||||
var tcpPorts bytes.Buffer
|
||||
var udpPorts bytes.Buffer
|
||||
var sctpPorts bytes.Buffer
|
||||
done := map[string]map[int]bool{
|
||||
"tcp": map[int]bool{},
|
||||
"udp": map[int]bool{},
|
||||
"sctp": map[int]bool{},
|
||||
}
|
||||
for _, r := range registry.Records {
|
||||
port, err := strconv.Atoi(r.Number)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
if r.Name == "" {
|
||||
continue
|
||||
}
|
||||
var b *bytes.Buffer
|
||||
switch r.Protocol {
|
||||
case "tcp":
|
||||
b = &tcpPorts
|
||||
case "udp":
|
||||
b = &udpPorts
|
||||
case "sctp":
|
||||
b = &sctpPorts
|
||||
default:
|
||||
continue
|
||||
}
|
||||
if done[r.Protocol][port] {
|
||||
continue
|
||||
}
|
||||
done[r.Protocol][port] = true
|
||||
fmt.Fprintf(b, "\t%d: %q,\n", port, r.Name)
|
||||
}
|
||||
fmt.Fprintln(os.Stderr, "Writing results to stdout")
|
||||
fmt.Printf(fmtString, time.Now(), *url, tcpPorts.String(), udpPorts.String(), sctpPorts.String())
|
||||
}
|
@ -0,0 +1,3 @@
|
||||
#!/bin/bash
|
||||
|
||||
for i in *.go; do golint $i | grep -q . || echo $i; done > .linted
|
@ -0,0 +1,98 @@
|
||||
// Copyright 2016 Google, Inc. All rights reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the LICENSE file in the root of the source
|
||||
// tree.
|
||||
|
||||
package layers
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
|
||||
"github.com/google/gopacket"
|
||||
)
|
||||
|
||||
// Geneve is specifed here https://tools.ietf.org/html/draft-ietf-nvo3-geneve-03
|
||||
// Geneve Header:
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// |Ver| Opt Len |O|C| Rsvd. | Protocol Type |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | Virtual Network Identifier (VNI) | Reserved |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | Variable Length Options |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
type Geneve struct {
|
||||
BaseLayer
|
||||
Version uint8 // 2 bits
|
||||
OptionsLength uint8 // 6 bits
|
||||
OAMPacket bool // 1 bits
|
||||
CriticalOption bool // 1 bits
|
||||
Protocol EthernetType // 16 bits
|
||||
VNI uint32 // 24bits
|
||||
Options []*GeneveOption
|
||||
}
|
||||
|
||||
// Geneve Tunnel Options
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | Option Class | Type |R|R|R| Length |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | Variable Option Data |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
type GeneveOption struct {
|
||||
Class uint16 // 16 bits
|
||||
Type uint8 // 8 bits
|
||||
Flags uint8 // 3 bits
|
||||
Length uint8 // 5 bits
|
||||
Data []byte
|
||||
}
|
||||
|
||||
// LayerType returns LayerTypeGeneve
|
||||
func (gn *Geneve) LayerType() gopacket.LayerType { return LayerTypeGeneve }
|
||||
|
||||
func decodeGeneveOption(data []byte, gn *Geneve) (*GeneveOption, uint8) {
|
||||
opt := &GeneveOption{}
|
||||
|
||||
opt.Class = binary.BigEndian.Uint16(data[0:1])
|
||||
opt.Type = data[2]
|
||||
opt.Flags = data[3] >> 4
|
||||
opt.Length = data[3] & 0xf
|
||||
|
||||
copy(opt.Data, data[4:opt.Length])
|
||||
|
||||
return opt, 4 + opt.Length
|
||||
}
|
||||
|
||||
func (gn *Geneve) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
|
||||
gn.Version = data[0] >> 7
|
||||
gn.OptionsLength = data[0] & 0x3f
|
||||
|
||||
gn.OAMPacket = data[1]&0x80 > 0
|
||||
gn.CriticalOption = data[1]&0x40 > 0
|
||||
gn.Protocol = EthernetType(binary.BigEndian.Uint16(data[2:4]))
|
||||
|
||||
var buf [4]byte
|
||||
copy(buf[1:], data[4:7])
|
||||
gn.VNI = binary.BigEndian.Uint32(buf[:])
|
||||
|
||||
offset, length := uint8(8), gn.OptionsLength
|
||||
for length > 0 {
|
||||
opt, len := decodeGeneveOption(data[offset:], gn)
|
||||
gn.Options = append(gn.Options, opt)
|
||||
|
||||
length -= len
|
||||
offset += len
|
||||
}
|
||||
|
||||
gn.BaseLayer = BaseLayer{data[:offset], data[offset:]}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (gn *Geneve) NextLayerType() gopacket.LayerType {
|
||||
return gn.Protocol.LayerType()
|
||||
}
|
||||
|
||||
func decodeGeneve(data []byte, p gopacket.PacketBuilder) error {
|
||||
gn := &Geneve{}
|
||||
return decodingLayerDecoder(gn, data, p)
|
||||
}
|
@ -0,0 +1,185 @@
|
||||
// Copyright 2012 Google, Inc. All rights reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the LICENSE file in the root of the source
|
||||
// tree.
|
||||
|
||||
package layers
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
|
||||
"github.com/google/gopacket"
|
||||
)
|
||||
|
||||
// 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
|
||||
*GRERouting
|
||||
}
|
||||
|
||||
// GRERouting is GRE routing information, present if the RoutingPresent flag is
|
||||
// set.
|
||||
type GRERouting struct {
|
||||
AddressFamily uint16
|
||||
SREOffset, SRELength uint8
|
||||
RoutingInformation []byte
|
||||
Next *GRERouting
|
||||
}
|
||||
|
||||
// LayerType returns gopacket.LayerTypeGRE.
|
||||
func (g *GRE) LayerType() gopacket.LayerType { return LayerTypeGRE }
|
||||
|
||||
// DecodeFromBytes decodes the given bytes into this layer.
|
||||
func (g *GRE) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
|
||||
g.ChecksumPresent = data[0]&0x80 != 0
|
||||
g.RoutingPresent = data[0]&0x40 != 0
|
||||
g.KeyPresent = data[0]&0x20 != 0
|
||||
g.SeqPresent = data[0]&0x10 != 0
|
||||
g.StrictSourceRoute = data[0]&0x08 != 0
|
||||
g.RecursionControl = data[0] & 0x7
|
||||
g.Flags = data[1] >> 3
|
||||
g.Version = data[1] & 0x7
|
||||
g.Protocol = EthernetType(binary.BigEndian.Uint16(data[2:4]))
|
||||
offset := 4
|
||||
if g.ChecksumPresent || g.RoutingPresent {
|
||||
g.Checksum = binary.BigEndian.Uint16(data[offset : offset+2])
|
||||
g.Offset = binary.BigEndian.Uint16(data[offset+2 : offset+4])
|
||||
offset += 4
|
||||
}
|
||||
if g.KeyPresent {
|
||||
g.Key = binary.BigEndian.Uint32(data[offset : offset+4])
|
||||
offset += 4
|
||||
}
|
||||
if g.SeqPresent {
|
||||
g.Seq = binary.BigEndian.Uint32(data[offset : offset+4])
|
||||
offset += 4
|
||||
}
|
||||
if g.RoutingPresent {
|
||||
tail := &g.GRERouting
|
||||
for {
|
||||
sre := &GRERouting{
|
||||
AddressFamily: binary.BigEndian.Uint16(data[offset : offset+2]),
|
||||
SREOffset: data[offset+2],
|
||||
SRELength: data[offset+3],
|
||||
}
|
||||
sre.RoutingInformation = data[offset+4 : offset+4+int(sre.SRELength)]
|
||||
offset += 4 + int(sre.SRELength)
|
||||
if sre.AddressFamily == 0 && sre.SRELength == 0 {
|
||||
break
|
||||
}
|
||||
(*tail) = sre
|
||||
tail = &sre.Next
|
||||
}
|
||||
}
|
||||
g.BaseLayer = BaseLayer{data[:offset], data[offset:]}
|
||||
return nil
|
||||
}
|
||||
|
||||
// SerializeTo writes the serialized form of this layer into the SerializationBuffer,
|
||||
// implementing gopacket.SerializableLayer. See the docs for gopacket.SerializableLayer for more info.
|
||||
func (g *GRE) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
|
||||
size := 4
|
||||
if g.ChecksumPresent || g.RoutingPresent {
|
||||
size += 4
|
||||
}
|
||||
if g.KeyPresent {
|
||||
size += 4
|
||||
}
|
||||
if g.SeqPresent {
|
||||
size += 4
|
||||
}
|
||||
if g.RoutingPresent {
|
||||
r := g.GRERouting
|
||||
for r != nil {
|
||||
size += 4 + int(r.SRELength)
|
||||
r = r.Next
|
||||
}
|
||||
size += 4
|
||||
}
|
||||
buf, err := b.PrependBytes(size)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Reset any potentially dirty memory in the first 2 bytes, as these use OR to set flags.
|
||||
buf[0] = 0
|
||||
buf[1] = 0
|
||||
if g.ChecksumPresent {
|
||||
buf[0] |= 0x80
|
||||
}
|
||||
if g.RoutingPresent {
|
||||
buf[0] |= 0x40
|
||||
}
|
||||
if g.KeyPresent {
|
||||
buf[0] |= 0x20
|
||||
}
|
||||
if g.SeqPresent {
|
||||
buf[0] |= 0x10
|
||||
}
|
||||
if g.StrictSourceRoute {
|
||||
buf[0] |= 0x08
|
||||
}
|
||||
buf[0] |= g.RecursionControl
|
||||
buf[1] |= g.Flags << 3
|
||||
buf[1] |= g.Version
|
||||
binary.BigEndian.PutUint16(buf[2:4], uint16(g.Protocol))
|
||||
offset := 4
|
||||
if g.ChecksumPresent || g.RoutingPresent {
|
||||
// Don't write the checksum value yet, as we may need to compute it,
|
||||
// which requires the entire header be complete.
|
||||
// Instead we zeroize the memory in case it is dirty.
|
||||
buf[offset] = 0
|
||||
buf[offset+1] = 0
|
||||
binary.BigEndian.PutUint16(buf[offset+2:offset+4], g.Offset)
|
||||
offset += 4
|
||||
}
|
||||
if g.KeyPresent {
|
||||
binary.BigEndian.PutUint32(buf[offset:offset+4], g.Key)
|
||||
offset += 4
|
||||
}
|
||||
if g.SeqPresent {
|
||||
binary.BigEndian.PutUint32(buf[offset:offset+4], g.Seq)
|
||||
offset += 4
|
||||
}
|
||||
if g.RoutingPresent {
|
||||
sre := g.GRERouting
|
||||
for sre != nil {
|
||||
binary.BigEndian.PutUint16(buf[offset:offset+2], sre.AddressFamily)
|
||||
buf[offset+2] = sre.SREOffset
|
||||
buf[offset+3] = sre.SRELength
|
||||
copy(buf[offset+4:offset+4+int(sre.SRELength)], sre.RoutingInformation)
|
||||
offset += 4 + int(sre.SRELength)
|
||||
sre = sre.Next
|
||||
}
|
||||
// Terminate routing field with a "NULL" SRE.
|
||||
binary.BigEndian.PutUint32(buf[offset:offset+4], 0)
|
||||
}
|
||||
if g.ChecksumPresent {
|
||||
if opts.ComputeChecksums {
|
||||
g.Checksum = tcpipChecksum(b.Bytes(), 0)
|
||||
}
|
||||
|
||||
binary.BigEndian.PutUint16(buf[4:6], g.Checksum)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// CanDecode returns the set of layer types that this DecodingLayer can decode.
|
||||
func (g *GRE) CanDecode() gopacket.LayerClass {
|
||||
return LayerTypeGRE
|
||||
}
|
||||
|
||||
// NextLayerType returns the layer type contained by this DecodingLayer.
|
||||
func (g *GRE) NextLayerType() gopacket.LayerType {
|
||||
return g.Protocol.LayerType()
|
||||
}
|
||||
|
||||
func decodeGRE(data []byte, p gopacket.PacketBuilder) error {
|
||||
g := &GRE{}
|
||||
return decodingLayerDecoder(g, data, p)
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,267 @@
|
||||
// Copyright 2012 Google, Inc. All rights reserved.
|
||||
// Copyright 2009-2011 Andreas Krennmair. 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"
|
||||
"fmt"
|
||||
"reflect"
|
||||
|
||||
"github.com/google/gopacket"
|
||||
)
|
||||
|
||||
const (
|
||||
ICMPv4TypeEchoReply = 0
|
||||
ICMPv4TypeDestinationUnreachable = 3
|
||||
ICMPv4TypeSourceQuench = 4
|
||||
ICMPv4TypeRedirect = 5
|
||||
ICMPv4TypeEchoRequest = 8
|
||||
ICMPv4TypeRouterAdvertisement = 9
|
||||
ICMPv4TypeRouterSolicitation = 10
|
||||
ICMPv4TypeTimeExceeded = 11
|
||||
ICMPv4TypeParameterProblem = 12
|
||||
ICMPv4TypeTimestampRequest = 13
|
||||
ICMPv4TypeTimestampReply = 14
|
||||
ICMPv4TypeInfoRequest = 15
|
||||
ICMPv4TypeInfoReply = 16
|
||||
ICMPv4TypeAddressMaskRequest = 17
|
||||
ICMPv4TypeAddressMaskReply = 18
|
||||
)
|
||||
|
||||
const (
|
||||
// DestinationUnreachable
|
||||
ICMPv4CodeNet = 0
|
||||
ICMPv4CodeHost = 1
|
||||
ICMPv4CodeProtocol = 2
|
||||
ICMPv4CodePort = 3
|
||||
ICMPv4CodeFragmentationNeeded = 4
|
||||
ICMPv4CodeSourceRoutingFailed = 5
|
||||
ICMPv4CodeNetUnknown = 6
|
||||
ICMPv4CodeHostUnknown = 7
|
||||
ICMPv4CodeSourceIsolated = 8
|
||||
ICMPv4CodeNetAdminProhibited = 9
|
||||
ICMPv4CodeHostAdminProhibited = 10
|
||||
ICMPv4CodeNetTOS = 11
|
||||
ICMPv4CodeHostTOS = 12
|
||||
ICMPv4CodeCommAdminProhibited = 13
|
||||
ICMPv4CodeHostPrecedence = 14
|
||||
ICMPv4CodePrecedenceCutoff = 15
|
||||
|
||||
// TimeExceeded
|
||||
ICMPv4CodeTTLExceeded = 0
|
||||
ICMPv4CodeFragmentReassemblyTimeExceeded = 1
|
||||
|
||||
// ParameterProblem
|
||||
ICMPv4CodePointerIndicatesError = 0
|
||||
ICMPv4CodeMissingOption = 1
|
||||
ICMPv4CodeBadLength = 2
|
||||
|
||||
// Redirect
|
||||
// ICMPv4CodeNet = same as for DestinationUnreachable
|
||||
// ICMPv4CodeHost = same as for DestinationUnreachable
|
||||
ICMPv4CodeTOSNet = 2
|
||||
ICMPv4CodeTOSHost = 3
|
||||
)
|
||||
|
||||
type icmpv4TypeCodeInfoStruct struct {
|
||||
typeStr string
|
||||
codeStr *map[uint8]string
|
||||
}
|
||||
|
||||
var (
|
||||
icmpv4TypeCodeInfo = map[uint8]icmpv4TypeCodeInfoStruct{
|
||||
ICMPv4TypeDestinationUnreachable: icmpv4TypeCodeInfoStruct{
|
||||
"DestinationUnreachable", &map[uint8]string{
|
||||
ICMPv4CodeNet: "Net",
|
||||
ICMPv4CodeHost: "Host",
|
||||
ICMPv4CodeProtocol: "Protocol",
|
||||
ICMPv4CodePort: "Port",
|
||||
ICMPv4CodeFragmentationNeeded: "FragmentationNeeded",
|
||||
ICMPv4CodeSourceRoutingFailed: "SourceRoutingFailed",
|
||||
ICMPv4CodeNetUnknown: "NetUnknown",
|
||||
ICMPv4CodeHostUnknown: "HostUnknown",
|
||||
ICMPv4CodeSourceIsolated: "SourceIsolated",
|
||||
ICMPv4CodeNetAdminProhibited: "NetAdminProhibited",
|
||||
ICMPv4CodeHostAdminProhibited: "HostAdminProhibited",
|
||||
ICMPv4CodeNetTOS: "NetTOS",
|
||||
ICMPv4CodeHostTOS: "HostTOS",
|
||||
ICMPv4CodeCommAdminProhibited: "CommAdminProhibited",
|
||||
ICMPv4CodeHostPrecedence: "HostPrecedence",
|
||||
ICMPv4CodePrecedenceCutoff: "PrecedenceCutoff",
|
||||
},
|
||||
},
|
||||
ICMPv4TypeTimeExceeded: icmpv4TypeCodeInfoStruct{
|
||||
"TimeExceeded", &map[uint8]string{
|
||||
ICMPv4CodeTTLExceeded: "TTLExceeded",
|
||||
ICMPv4CodeFragmentReassemblyTimeExceeded: "FragmentReassemblyTimeExceeded",
|
||||
},
|
||||
},
|
||||
ICMPv4TypeParameterProblem: icmpv4TypeCodeInfoStruct{
|
||||
"ParameterProblem", &map[uint8]string{
|
||||
ICMPv4CodePointerIndicatesError: "PointerIndicatesError",
|
||||
ICMPv4CodeMissingOption: "MissingOption",
|
||||
ICMPv4CodeBadLength: "BadLength",
|
||||
},
|
||||
},
|
||||
ICMPv4TypeSourceQuench: icmpv4TypeCodeInfoStruct{
|
||||
"SourceQuench", nil,
|
||||
},
|
||||
ICMPv4TypeRedirect: icmpv4TypeCodeInfoStruct{
|
||||
"Redirect", &map[uint8]string{
|
||||
ICMPv4CodeNet: "Net",
|
||||
ICMPv4CodeHost: "Host",
|
||||
ICMPv4CodeTOSNet: "TOS+Net",
|
||||
ICMPv4CodeTOSHost: "TOS+Host",
|
||||
},
|
||||
},
|
||||
ICMPv4TypeEchoRequest: icmpv4TypeCodeInfoStruct{
|
||||
"EchoRequest", nil,
|
||||
},
|
||||
ICMPv4TypeEchoReply: icmpv4TypeCodeInfoStruct{
|
||||
"EchoReply", nil,
|
||||
},
|
||||
ICMPv4TypeTimestampRequest: icmpv4TypeCodeInfoStruct{
|
||||
"TimestampRequest", nil,
|
||||
},
|
||||
ICMPv4TypeTimestampReply: icmpv4TypeCodeInfoStruct{
|
||||
"TimestampReply", nil,
|
||||
},
|
||||
ICMPv4TypeInfoRequest: icmpv4TypeCodeInfoStruct{
|
||||
"InfoRequest", nil,
|
||||
},
|
||||
ICMPv4TypeInfoReply: icmpv4TypeCodeInfoStruct{
|
||||
"InfoReply", nil,
|
||||
},
|
||||
ICMPv4TypeRouterSolicitation: icmpv4TypeCodeInfoStruct{
|
||||
"RouterSolicitation", nil,
|
||||
},
|
||||
ICMPv4TypeRouterAdvertisement: icmpv4TypeCodeInfoStruct{
|
||||
"RouterAdvertisement", nil,
|
||||
},
|
||||
ICMPv4TypeAddressMaskRequest: icmpv4TypeCodeInfoStruct{
|
||||
"AddressMaskRequest", nil,
|
||||
},
|
||||
ICMPv4TypeAddressMaskReply: icmpv4TypeCodeInfoStruct{
|
||||
"AddressMaskReply", nil,
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
type ICMPv4TypeCode uint16
|
||||
|
||||
// Type returns the ICMPv4 type field.
|
||||
func (a ICMPv4TypeCode) Type() uint8 {
|
||||
return uint8(a >> 8)
|
||||
}
|
||||
|
||||
// Code returns the ICMPv4 code field.
|
||||
func (a ICMPv4TypeCode) Code() uint8 {
|
||||
return uint8(a)
|
||||
}
|
||||
|
||||
func (a ICMPv4TypeCode) String() string {
|
||||
t, c := a.Type(), a.Code()
|
||||
strInfo, ok := icmpv4TypeCodeInfo[t]
|
||||
if !ok {
|
||||
// Unknown ICMPv4 type field
|
||||
return fmt.Sprintf("%d(%d)", t, c)
|
||||
}
|
||||
typeStr := strInfo.typeStr
|
||||
if strInfo.codeStr == nil && c == 0 {
|
||||
// The ICMPv4 type does not make use of the code field
|
||||
return fmt.Sprintf("%s", strInfo.typeStr)
|
||||
}
|
||||
if strInfo.codeStr == nil && c != 0 {
|
||||
// The ICMPv4 type does not make use of the code field, but it is present anyway
|
||||
return fmt.Sprintf("%s(Code: %d)", typeStr, c)
|
||||
}
|
||||
codeStr, ok := (*strInfo.codeStr)[c]
|
||||
if !ok {
|
||||
// We don't know this ICMPv4 code; print the numerical value
|
||||
return fmt.Sprintf("%s(Code: %d)", typeStr, c)
|
||||
}
|
||||
return fmt.Sprintf("%s(%s)", typeStr, codeStr)
|
||||
}
|
||||
|
||||
func (a ICMPv4TypeCode) GoString() string {
|
||||
t := reflect.TypeOf(a)
|
||||
return fmt.Sprintf("%s(%d, %d)", t.String(), a.Type(), a.Code())
|
||||
}
|
||||
|
||||
// SerializeTo writes the ICMPv4TypeCode value to the 'bytes' buffer.
|
||||
func (a ICMPv4TypeCode) SerializeTo(bytes []byte) {
|
||||
binary.BigEndian.PutUint16(bytes, uint16(a))
|
||||
}
|
||||
|
||||
// CreateICMPv4TypeCode is a convenience function to create an ICMPv4TypeCode
|
||||
// gopacket type from the ICMPv4 type and code values.
|
||||
func CreateICMPv4TypeCode(typ uint8, code uint8) ICMPv4TypeCode {
|
||||
return ICMPv4TypeCode(binary.BigEndian.Uint16([]byte{typ, code}))
|
||||
}
|
||||
|
||||
// ICMPv4 is the layer for IPv4 ICMP packet data.
|
||||
type ICMPv4 struct {
|
||||
BaseLayer
|
||||
TypeCode ICMPv4TypeCode
|
||||
Checksum uint16
|
||||
Id uint16
|
||||
Seq uint16
|
||||
}
|
||||
|
||||
// LayerType returns LayerTypeICMPv4.
|
||||
func (i *ICMPv4) LayerType() gopacket.LayerType { return LayerTypeICMPv4 }
|
||||
|
||||
// DecodeFromBytes decodes the given bytes into this layer.
|
||||
func (i *ICMPv4) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
|
||||
if len(data) < 8 {
|
||||
df.SetTruncated()
|
||||
return errors.New("ICMP layer less then 8 bytes for ICMPv4 packet")
|
||||
}
|
||||
i.TypeCode = CreateICMPv4TypeCode(data[0], data[1])
|
||||
i.Checksum = binary.BigEndian.Uint16(data[2:4])
|
||||
i.Id = binary.BigEndian.Uint16(data[4:6])
|
||||
i.Seq = binary.BigEndian.Uint16(data[6:8])
|
||||
i.BaseLayer = BaseLayer{data[:8], data[8:]}
|
||||
return nil
|
||||
}
|
||||
|
||||
// SerializeTo writes the serialized form of this layer into the
|
||||
// SerializationBuffer, implementing gopacket.SerializableLayer.
|
||||
// See the docs for gopacket.SerializableLayer for more info.
|
||||
func (i *ICMPv4) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
|
||||
bytes, err := b.PrependBytes(8)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
i.TypeCode.SerializeTo(bytes)
|
||||
binary.BigEndian.PutUint16(bytes[4:], i.Id)
|
||||
binary.BigEndian.PutUint16(bytes[6:], i.Seq)
|
||||
if opts.ComputeChecksums {
|
||||
bytes[2] = 0
|
||||
bytes[3] = 0
|
||||
i.Checksum = tcpipChecksum(b.Bytes(), 0)
|
||||
}
|
||||
binary.BigEndian.PutUint16(bytes[2:], i.Checksum)
|
||||
return nil
|
||||
}
|
||||
|
||||
// CanDecode returns the set of layer types that this DecodingLayer can decode.
|
||||
func (i *ICMPv4) CanDecode() gopacket.LayerClass {
|
||||
return LayerTypeICMPv4
|
||||
}
|
||||
|
||||
// NextLayerType returns the layer type contained by this DecodingLayer.
|
||||
func (i *ICMPv4) NextLayerType() gopacket.LayerType {
|
||||
return gopacket.LayerTypePayload
|
||||
}
|
||||
|
||||
func decodeICMPv4(data []byte, p gopacket.PacketBuilder) error {
|
||||
i := &ICMPv4{}
|
||||
return decodingLayerDecoder(i, data, p)
|
||||
}
|
@ -0,0 +1,231 @@
|
||||
// Copyright 2012 Google, Inc. All rights reserved.
|
||||
// Copyright 2009-2011 Andreas Krennmair. 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"
|
||||
"fmt"
|
||||
"reflect"
|
||||
|
||||
"github.com/google/gopacket"
|
||||
)
|
||||
|
||||
const (
|
||||
// The following are from RFC 4443
|
||||
ICMPv6TypeDestinationUnreachable = 1
|
||||
ICMPv6TypePacketTooBig = 2
|
||||
ICMPv6TypeTimeExceeded = 3
|
||||
ICMPv6TypeParameterProblem = 4
|
||||
ICMPv6TypeEchoRequest = 128
|
||||
ICMPv6TypeEchoReply = 129
|
||||
// The following are from RFC 4861
|
||||
ICMPv6TypeRouterSolicitation = 133
|
||||
ICMPv6TypeRouterAdvertisement = 134
|
||||
ICMPv6TypeNeighborSolicitation = 135
|
||||
ICMPv6TypeNeighborAdvertisement = 136
|
||||
ICMPv6TypeRedirect = 137
|
||||
)
|
||||
|
||||
const (
|
||||
// DestinationUnreachable
|
||||
ICMPv6CodeNoRouteToDst = 0
|
||||
ICMPv6CodeAdminProhibited = 1
|
||||
ICMPv6CodeBeyondScopeOfSrc = 2
|
||||
ICMPv6CodeAddressUnreachable = 3
|
||||
ICMPv6CodePortUnreachable = 4
|
||||
ICMPv6CodeSrcAddressFailedPolicy = 5
|
||||
ICMPv6CodeRejectRouteToDst = 6
|
||||
|
||||
// TimeExceeded
|
||||
ICMPv6CodeHopLimitExceeded = 0
|
||||
ICMPv6CodeFragmentReassemblyTimeExceeded = 1
|
||||
|
||||
// ParameterProblem
|
||||
ICMPv6CodeErroneousHeaderField = 0
|
||||
ICMPv6CodeUnrecognizedNextHeader = 1
|
||||
ICMPv6CodeUnrecognizedIPv6Option = 2
|
||||
)
|
||||
|
||||
type icmpv6TypeCodeInfoStruct struct {
|
||||
typeStr string
|
||||
codeStr *map[uint8]string
|
||||
}
|
||||
|
||||
var (
|
||||
icmpv6TypeCodeInfo = map[uint8]icmpv6TypeCodeInfoStruct{
|
||||
ICMPv6TypeDestinationUnreachable: icmpv6TypeCodeInfoStruct{
|
||||
"DestinationUnreachable", &map[uint8]string{
|
||||
ICMPv6CodeNoRouteToDst: "NoRouteToDst",
|
||||
ICMPv6CodeAdminProhibited: "AdminProhibited",
|
||||
ICMPv6CodeBeyondScopeOfSrc: "BeyondScopeOfSrc",
|
||||
ICMPv6CodeAddressUnreachable: "AddressUnreachable",
|
||||
ICMPv6CodePortUnreachable: "PortUnreachable",
|
||||
ICMPv6CodeSrcAddressFailedPolicy: "SrcAddressFailedPolicy",
|
||||
ICMPv6CodeRejectRouteToDst: "RejectRouteToDst",
|
||||
},
|
||||
},
|
||||
ICMPv6TypePacketTooBig: icmpv6TypeCodeInfoStruct{
|
||||
"PacketTooBig", nil,
|
||||
},
|
||||
ICMPv6TypeTimeExceeded: icmpv6TypeCodeInfoStruct{
|
||||
"TimeExceeded", &map[uint8]string{
|
||||
ICMPv6CodeHopLimitExceeded: "HopLimitExceeded",
|
||||
ICMPv6CodeFragmentReassemblyTimeExceeded: "FragmentReassemblyTimeExceeded",
|
||||
},
|
||||
},
|
||||
ICMPv6TypeParameterProblem: icmpv6TypeCodeInfoStruct{
|
||||
"ParameterProblem", &map[uint8]string{
|
||||
ICMPv6CodeErroneousHeaderField: "ErroneousHeaderField",
|
||||
ICMPv6CodeUnrecognizedNextHeader: "UnrecognizedNextHeader",
|
||||
ICMPv6CodeUnrecognizedIPv6Option: "UnrecognizedIPv6Option",
|
||||
},
|
||||
},
|
||||
ICMPv6TypeEchoRequest: icmpv6TypeCodeInfoStruct{
|
||||
"EchoRequest", nil,
|
||||
},
|
||||
ICMPv6TypeEchoReply: icmpv6TypeCodeInfoStruct{
|
||||
"EchoReply", nil,
|
||||
},
|
||||
ICMPv6TypeRouterSolicitation: icmpv6TypeCodeInfoStruct{
|
||||
"RouterSolicitation", nil,
|
||||
},
|
||||
ICMPv6TypeRouterAdvertisement: icmpv6TypeCodeInfoStruct{
|
||||
"RouterAdvertisement", nil,
|
||||
},
|
||||
ICMPv6TypeNeighborSolicitation: icmpv6TypeCodeInfoStruct{
|
||||
"NeighborSolicitation", nil,
|
||||
},
|
||||
ICMPv6TypeNeighborAdvertisement: icmpv6TypeCodeInfoStruct{
|
||||
"NeighborAdvertisement", nil,
|
||||
},
|
||||
ICMPv6TypeRedirect: icmpv6TypeCodeInfoStruct{
|
||||
"Redirect", nil,
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
type ICMPv6TypeCode uint16
|
||||
|
||||
// Type returns the ICMPv6 type field.
|
||||
func (a ICMPv6TypeCode) Type() uint8 {
|
||||
return uint8(a >> 8)
|
||||
}
|
||||
|
||||
// Code returns the ICMPv6 code field.
|
||||
func (a ICMPv6TypeCode) Code() uint8 {
|
||||
return uint8(a)
|
||||
}
|
||||
|
||||
func (a ICMPv6TypeCode) String() string {
|
||||
t, c := a.Type(), a.Code()
|
||||
strInfo, ok := icmpv6TypeCodeInfo[t]
|
||||
if !ok {
|
||||
// Unknown ICMPv6 type field
|
||||
return fmt.Sprintf("%d(%d)", t, c)
|
||||
}
|
||||
typeStr := strInfo.typeStr
|
||||
if strInfo.codeStr == nil && c == 0 {
|
||||
// The ICMPv6 type does not make use of the code field
|
||||
return fmt.Sprintf("%s", strInfo.typeStr)
|
||||
}
|
||||
if strInfo.codeStr == nil && c != 0 {
|
||||
// The ICMPv6 type does not make use of the code field, but it is present anyway
|
||||
return fmt.Sprintf("%s(Code: %d)", typeStr, c)
|
||||
}
|
||||
codeStr, ok := (*strInfo.codeStr)[c]
|
||||
if !ok {
|
||||
// We don't know this ICMPv6 code; print the numerical value
|
||||
return fmt.Sprintf("%s(Code: %d)", typeStr, c)
|
||||
}
|
||||
return fmt.Sprintf("%s(%s)", typeStr, codeStr)
|
||||
}
|
||||
|
||||
func (a ICMPv6TypeCode) GoString() string {
|
||||
t := reflect.TypeOf(a)
|
||||
return fmt.Sprintf("%s(%d, %d)", t.String(), a.Type(), a.Code())
|
||||
}
|
||||
|
||||
// SerializeTo writes the ICMPv6TypeCode value to the 'bytes' buffer.
|
||||
func (a ICMPv6TypeCode) SerializeTo(bytes []byte) {
|
||||
binary.BigEndian.PutUint16(bytes, uint16(a))
|
||||
}
|
||||
|
||||
// CreateICMPv6TypeCode is a convenience function to create an ICMPv6TypeCode
|
||||
// gopacket type from the ICMPv6 type and code values.
|
||||
func CreateICMPv6TypeCode(typ uint8, code uint8) ICMPv6TypeCode {
|
||||
return ICMPv6TypeCode(binary.BigEndian.Uint16([]byte{typ, code}))
|
||||
}
|
||||
|
||||
// ICMPv6 is the layer for IPv6 ICMP packet data
|
||||
type ICMPv6 struct {
|
||||
BaseLayer
|
||||
TypeCode ICMPv6TypeCode
|
||||
Checksum uint16
|
||||
TypeBytes []byte
|
||||
tcpipchecksum
|
||||
}
|
||||
|
||||
// LayerType returns LayerTypeICMPv6.
|
||||
func (i *ICMPv6) LayerType() gopacket.LayerType { return LayerTypeICMPv6 }
|
||||
|
||||
// DecodeFromBytes decodes the given bytes into this layer.
|
||||
func (i *ICMPv6) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
|
||||
if len(data) < 8 {
|
||||
df.SetTruncated()
|
||||
return errors.New("ICMP layer less then 8 bytes for ICMPv6 packet")
|
||||
}
|
||||
i.TypeCode = CreateICMPv6TypeCode(data[0], data[1])
|
||||
i.Checksum = binary.BigEndian.Uint16(data[2:4])
|
||||
i.TypeBytes = data[4:8]
|
||||
i.BaseLayer = BaseLayer{data[:8], data[8:]}
|
||||
return nil
|
||||
}
|
||||
|
||||
// SerializeTo writes the serialized form of this layer into the
|
||||
// SerializationBuffer, implementing gopacket.SerializableLayer.
|
||||
// See the docs for gopacket.SerializableLayer for more info.
|
||||
func (i *ICMPv6) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
|
||||
if i.TypeBytes == nil {
|
||||
i.TypeBytes = lotsOfZeros[:4]
|
||||
} else if len(i.TypeBytes) != 4 {
|
||||
return fmt.Errorf("invalid type bytes for ICMPv6 packet: %v", i.TypeBytes)
|
||||
}
|
||||
bytes, err := b.PrependBytes(8)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
i.TypeCode.SerializeTo(bytes)
|
||||
copy(bytes[4:8], i.TypeBytes)
|
||||
if opts.ComputeChecksums {
|
||||
bytes[2] = 0
|
||||
bytes[3] = 0
|
||||
csum, err := i.computeChecksum(b.Bytes(), IPProtocolICMPv6)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
i.Checksum = csum
|
||||
}
|
||||
binary.BigEndian.PutUint16(bytes[2:], i.Checksum)
|
||||
return nil
|
||||
}
|
||||
|
||||
// CanDecode returns the set of layer types that this DecodingLayer can decode.
|
||||
func (i *ICMPv6) CanDecode() gopacket.LayerClass {
|
||||
return LayerTypeICMPv6
|
||||
}
|
||||
|
||||
// NextLayerType returns the layer type contained by this DecodingLayer.
|
||||
func (i *ICMPv6) NextLayerType() gopacket.LayerType {
|
||||
return gopacket.LayerTypePayload
|
||||
}
|
||||
|
||||
func decodeICMPv6(data []byte, p gopacket.PacketBuilder) error {
|
||||
i := &ICMPv6{}
|
||||
return decodingLayerDecoder(i, data, p)
|
||||
}
|
@ -0,0 +1,355 @@
|
||||
// Copyright 2012 Google, Inc. All rights reserved.
|
||||
// Copyright 2009-2011 Andreas Krennmair. 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"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/google/gopacket"
|
||||
)
|
||||
|
||||
type IGMPType uint8
|
||||
|
||||
const (
|
||||
IGMPMembershipQuery IGMPType = 0x11 // General or group specific query
|
||||
IGMPMembershipReportV1 IGMPType = 0x12 // Version 1 Membership Report
|
||||
IGMPMembershipReportV2 IGMPType = 0x16 // Version 2 Membership Report
|
||||
IGMPLeaveGroup IGMPType = 0x17 // Leave Group
|
||||
IGMPMembershipReportV3 IGMPType = 0x22 // Version 3 Membership Report
|
||||
)
|
||||
|
||||
// String conversions for IGMP message types
|
||||
func (i IGMPType) String() string {
|
||||
switch i {
|
||||
case IGMPMembershipQuery:
|
||||
return "IGMP Membership Query"
|
||||
case IGMPMembershipReportV1:
|
||||
return "IGMPv1 Membership Report"
|
||||
case IGMPMembershipReportV2:
|
||||
return "IGMPv2 Membership Report"
|
||||
case IGMPMembershipReportV3:
|
||||
return "IGMPv3 Membership Report"
|
||||
case IGMPLeaveGroup:
|
||||
return "Leave Group"
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
type IGMPv3GroupRecordType uint8
|
||||
|
||||
const (
|
||||
IGMPIsIn IGMPv3GroupRecordType = 0x01 // Type MODE_IS_INCLUDE, source addresses x
|
||||
IGMPIsEx IGMPv3GroupRecordType = 0x02 // Type MODE_IS_EXCLUDE, source addresses x
|
||||
IGMPToIn IGMPv3GroupRecordType = 0x03 // Type CHANGE_TO_INCLUDE_MODE, source addresses x
|
||||
IGMPToEx IGMPv3GroupRecordType = 0x04 // Type CHANGE_TO_EXCLUDE_MODE, source addresses x
|
||||
IGMPAllow IGMPv3GroupRecordType = 0x05 // Type ALLOW_NEW_SOURCES, source addresses x
|
||||
IGMPBlock IGMPv3GroupRecordType = 0x06 // Type BLOCK_OLD_SOURCES, source addresses x
|
||||
)
|
||||
|
||||
func (i IGMPv3GroupRecordType) String() string {
|
||||
switch i {
|
||||
case IGMPIsIn:
|
||||
return "MODE_IS_INCLUDE"
|
||||
case IGMPIsEx:
|
||||
return "MODE_IS_EXCLUDE"
|
||||
case IGMPToIn:
|
||||
return "CHANGE_TO_INCLUDE_MODE"
|
||||
case IGMPToEx:
|
||||
return "CHANGE_TO_EXCLUDE_MODE"
|
||||
case IGMPAllow:
|
||||
return "ALLOW_NEW_SOURCES"
|
||||
case IGMPBlock:
|
||||
return "BLOCK_OLD_SOURCES"
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
// IGMP represents an IGMPv3 message.
|
||||
type IGMP struct {
|
||||
BaseLayer
|
||||
Type IGMPType
|
||||
MaxResponseTime time.Duration
|
||||
Checksum uint16
|
||||
GroupAddress net.IP
|
||||
SupressRouterProcessing bool
|
||||
RobustnessValue uint8
|
||||
IntervalTime time.Duration
|
||||
SourceAddresses []net.IP
|
||||
NumberOfGroupRecords uint16
|
||||
NumberOfSources uint16
|
||||
GroupRecords []IGMPv3GroupRecord
|
||||
Version uint8 // IGMP protocol version
|
||||
}
|
||||
|
||||
// IGMPv1or2 stores header details for an IGMPv1 or IGMPv2 packet.
|
||||
//
|
||||
// 0 1 2 3
|
||||
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | Type | Max Resp Time | Checksum |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | Group Address |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
type IGMPv1or2 struct {
|
||||
BaseLayer
|
||||
Type IGMPType // IGMP message type
|
||||
MaxResponseTime time.Duration // meaningful only in Membership Query messages
|
||||
Checksum uint16 // 16-bit checksum of entire ip payload
|
||||
GroupAddress net.IP // either 0 or an IP multicast address
|
||||
Version uint8
|
||||
}
|
||||
|
||||
// decodeResponse dissects IGMPv1 or IGMPv2 packet.
|
||||
func (i *IGMPv1or2) decodeResponse(data []byte) error {
|
||||
if len(data) < 8 {
|
||||
return errors.New("IGMP packet too small")
|
||||
}
|
||||
|
||||
i.MaxResponseTime = igmpTimeDecode(data[1])
|
||||
i.Checksum = binary.BigEndian.Uint16(data[2:4])
|
||||
i.GroupAddress = net.IP(data[4:8])
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// 0 1 2 3
|
||||
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | Type = 0x22 | Reserved | Checksum |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | Reserved | Number of Group Records (M) |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | |
|
||||
// . Group Record [1] .
|
||||
// | |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | |
|
||||
// . Group Record [2] .
|
||||
// | |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | |
|
||||
// . Group Record [M] .
|
||||
// | |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | Record Type | Aux Data Len | Number of Sources (N) |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | Multicast Address |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | Source Address [1] |
|
||||
// +- -+
|
||||
// | Source Address [2] |
|
||||
// +- -+
|
||||
// | Source Address [N] |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | |
|
||||
// . Auxiliary Data .
|
||||
// | |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
|
||||
// IGMPv3GroupRecord stores individual group records for a V3 Membership Report message.
|
||||
type IGMPv3GroupRecord struct {
|
||||
Type IGMPv3GroupRecordType
|
||||
AuxDataLen uint8 // this should always be 0 as per IGMPv3 spec.
|
||||
NumberOfSources uint16
|
||||
MulticastAddress net.IP
|
||||
SourceAddresses []net.IP
|
||||
AuxData uint32 // NOT USED
|
||||
}
|
||||
|
||||
func (i *IGMP) decodeIGMPv3MembershipReport(data []byte) error {
|
||||
if len(data) < 8 {
|
||||
return errors.New("IGMPv3 Membership Report too small #1")
|
||||
}
|
||||
|
||||
i.Checksum = binary.BigEndian.Uint16(data[2:4])
|
||||
i.NumberOfGroupRecords = binary.BigEndian.Uint16(data[6:8])
|
||||
|
||||
recordOffset := 8
|
||||
for j := 0; j < int(i.NumberOfGroupRecords); j++ {
|
||||
if len(data) < recordOffset+8 {
|
||||
return errors.New("IGMPv3 Membership Report too small #2")
|
||||
}
|
||||
|
||||
var gr IGMPv3GroupRecord
|
||||
gr.Type = IGMPv3GroupRecordType(data[recordOffset])
|
||||
gr.AuxDataLen = data[recordOffset+1]
|
||||
gr.NumberOfSources = binary.BigEndian.Uint16(data[recordOffset+2 : recordOffset+4])
|
||||
gr.MulticastAddress = net.IP(data[recordOffset+4 : recordOffset+8])
|
||||
|
||||
if len(data) < recordOffset+8+int(gr.NumberOfSources)*4 {
|
||||
return errors.New("IGMPv3 Membership Report too small #3")
|
||||
}
|
||||
|
||||
// append source address records.
|
||||
for i := 0; i < int(gr.NumberOfSources); i++ {
|
||||
sourceAddr := net.IP(data[recordOffset+8+i*4 : recordOffset+12+i*4])
|
||||
gr.SourceAddresses = append(gr.SourceAddresses, sourceAddr)
|
||||
}
|
||||
|
||||
i.GroupRecords = append(i.GroupRecords, gr)
|
||||
recordOffset += 8 + 4*int(gr.NumberOfSources)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// 0 1 2 3
|
||||
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | Type = 0x11 | Max Resp Code | Checksum |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | Group Address |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | Resv |S| QRV | QQIC | Number of Sources (N) |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | Source Address [1] |
|
||||
// +- -+
|
||||
// | Source Address [2] |
|
||||
// +- . -+
|
||||
// | Source Address [N] |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
//
|
||||
// decodeIGMPv3MembershipQuery parses the IGMPv3 message of type 0x11
|
||||
func (i *IGMP) decodeIGMPv3MembershipQuery(data []byte) error {
|
||||
if len(data) < 12 {
|
||||
return errors.New("IGMPv3 Membership Query too small #1")
|
||||
}
|
||||
|
||||
i.MaxResponseTime = igmpTimeDecode(data[1])
|
||||
i.Checksum = binary.BigEndian.Uint16(data[2:4])
|
||||
i.SupressRouterProcessing = data[8]&0x8 != 0
|
||||
i.GroupAddress = net.IP(data[4:8])
|
||||
i.RobustnessValue = data[8] & 0x7
|
||||
i.IntervalTime = igmpTimeDecode(data[9])
|
||||
i.NumberOfSources = binary.BigEndian.Uint16(data[10:12])
|
||||
|
||||
if len(data) < 12+int(i.NumberOfSources)*4 {
|
||||
return errors.New("IGMPv3 Membership Query too small #2")
|
||||
}
|
||||
|
||||
for j := 0; j < int(i.NumberOfSources); j++ {
|
||||
i.SourceAddresses = append(i.SourceAddresses, net.IP(data[12+j*4:16+j*4]))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// igmpTimeDecode decodes the duration created by the given byte, using the
|
||||
// algorithm in http://www.rfc-base.org/txt/rfc-3376.txt section 4.1.1.
|
||||
func igmpTimeDecode(t uint8) time.Duration {
|
||||
if t&0x80 == 0 {
|
||||
return time.Millisecond * 100 * time.Duration(t)
|
||||
}
|
||||
mant := (t & 0x70) >> 4
|
||||
exp := t & 0x0F
|
||||
return time.Millisecond * 100 * time.Duration((mant|0x10)<<(exp+3))
|
||||
}
|
||||
|
||||
// LayerType returns LayerTypeIGMP for the V1,2,3 message protocol formats.
|
||||
func (i *IGMP) LayerType() gopacket.LayerType { return LayerTypeIGMP }
|
||||
func (i *IGMPv1or2) LayerType() gopacket.LayerType { return LayerTypeIGMP }
|
||||
|
||||
func (i *IGMPv1or2) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
|
||||
if len(data) < 8 {
|
||||
return errors.New("IGMP Packet too small")
|
||||
}
|
||||
|
||||
i.Type = IGMPType(data[0])
|
||||
i.MaxResponseTime = igmpTimeDecode(data[1])
|
||||
i.Checksum = binary.BigEndian.Uint16(data[2:4])
|
||||
i.GroupAddress = net.IP(data[4:8])
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (i *IGMPv1or2) NextLayerType() gopacket.LayerType {
|
||||
return gopacket.LayerTypeZero
|
||||
}
|
||||
|
||||
func (i *IGMPv1or2) CanDecode() gopacket.LayerClass {
|
||||
return LayerTypeIGMP
|
||||
}
|
||||
|
||||
// DecodeFromBytes decodes the given bytes into this layer.
|
||||
func (i *IGMP) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
|
||||
if len(data) < 1 {
|
||||
return errors.New("IGMP packet is too small")
|
||||
}
|
||||
|
||||
// common IGMP header values between versions 1..3 of IGMP specification..
|
||||
i.Type = IGMPType(data[0])
|
||||
|
||||
switch i.Type {
|
||||
case IGMPMembershipQuery:
|
||||
i.decodeIGMPv3MembershipQuery(data)
|
||||
case IGMPMembershipReportV3:
|
||||
i.decodeIGMPv3MembershipReport(data)
|
||||
default:
|
||||
return errors.New("unsupported IGMP type")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// CanDecode returns the set of layer types that this DecodingLayer can decode.
|
||||
func (i *IGMP) CanDecode() gopacket.LayerClass {
|
||||
return LayerTypeIGMP
|
||||
}
|
||||
|
||||
// NextLayerType returns the layer type contained by this DecodingLayer.
|
||||
func (i *IGMP) NextLayerType() gopacket.LayerType {
|
||||
return gopacket.LayerTypeZero
|
||||
}
|
||||
|
||||
// decodeIGMP will parse IGMP v1,2 or 3 protocols. Checks against the
|
||||
// IGMP type are performed against byte[0], logic then iniitalizes and
|
||||
// passes the appropriate struct (IGMP or IGMPv1or2) to
|
||||
// decodingLayerDecoder.
|
||||
func decodeIGMP(data []byte, p gopacket.PacketBuilder) error {
|
||||
if len(data) < 1 {
|
||||
return errors.New("IGMP packet is too small")
|
||||
}
|
||||
|
||||
// byte 0 contains IGMP message type.
|
||||
switch IGMPType(data[0]) {
|
||||
case IGMPMembershipQuery:
|
||||
// IGMPv3 Membership Query payload is >= 12
|
||||
if len(data) >= 12 {
|
||||
i := &IGMP{Version: 3}
|
||||
return decodingLayerDecoder(i, data, p)
|
||||
} else if len(data) == 8 {
|
||||
i := &IGMPv1or2{}
|
||||
if data[1] == 0x00 {
|
||||
i.Version = 1 // IGMPv1 has a query length of 8 and MaxResp = 0
|
||||
} else {
|
||||
i.Version = 2 // IGMPv2 has a query length of 8 and MaxResp != 0
|
||||
}
|
||||
|
||||
return decodingLayerDecoder(i, data, p)
|
||||
}
|
||||
case IGMPMembershipReportV3:
|
||||
i := &IGMP{Version: 3}
|
||||
return decodingLayerDecoder(i, data, p)
|
||||
case IGMPMembershipReportV1:
|
||||
i := &IGMPv1or2{Version: 1}
|
||||
return decodingLayerDecoder(i, data, p)
|
||||
case IGMPLeaveGroup, IGMPMembershipReportV2:
|
||||
// leave group and Query Report v2 used in IGMPv2 only.
|
||||
i := &IGMPv1or2{Version: 2}
|
||||
return decodingLayerDecoder(i, data, p)
|
||||
default:
|
||||
}
|
||||
|
||||
return errors.New("Unable to determine IGMP type.")
|
||||
}
|
@ -0,0 +1,311 @@
|
||||
// Copyright 2012 Google, Inc. All rights reserved.
|
||||
// Copyright 2009-2011 Andreas Krennmair. 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"
|
||||
"fmt"
|
||||
"net"
|
||||
"strings"
|
||||
|
||||
"github.com/google/gopacket"
|
||||
)
|
||||
|
||||
type IPv4Flag uint8
|
||||
|
||||
const (
|
||||
IPv4EvilBit IPv4Flag = 1 << 2 // http://tools.ietf.org/html/rfc3514 ;)
|
||||
IPv4DontFragment IPv4Flag = 1 << 1
|
||||
IPv4MoreFragments IPv4Flag = 1 << 0
|
||||
)
|
||||
|
||||
func (f IPv4Flag) String() string {
|
||||
var s []string
|
||||
if f&IPv4EvilBit != 0 {
|
||||
s = append(s, "Evil")
|
||||
}
|
||||
if f&IPv4DontFragment != 0 {
|
||||
s = append(s, "DF")
|
||||
}
|
||||
if f&IPv4MoreFragments != 0 {
|
||||
s = append(s, "MF")
|
||||
}
|
||||
return strings.Join(s, "|")
|
||||
}
|
||||
|
||||
// IPv4 is the header of an IP packet.
|
||||
type IPv4 struct {
|
||||
BaseLayer
|
||||
Version uint8
|
||||
IHL uint8
|
||||
TOS uint8
|
||||
Length uint16
|
||||
Id uint16
|
||||
Flags IPv4Flag
|
||||
FragOffset uint16
|
||||
TTL uint8
|
||||
Protocol IPProtocol
|
||||
Checksum uint16
|
||||
SrcIP net.IP
|
||||
DstIP net.IP
|
||||
Options []IPv4Option
|
||||
Padding []byte
|
||||
}
|
||||
|
||||
// LayerType returns LayerTypeIPv4
|
||||
func (i *IPv4) LayerType() gopacket.LayerType { return LayerTypeIPv4 }
|
||||
func (i *IPv4) NetworkFlow() gopacket.Flow {
|
||||
return gopacket.NewFlow(EndpointIPv4, i.SrcIP, i.DstIP)
|
||||
}
|
||||
|
||||
type IPv4Option struct {
|
||||
OptionType uint8
|
||||
OptionLength uint8
|
||||
OptionData []byte
|
||||
}
|
||||
|
||||
func (i IPv4Option) String() string {
|
||||
return fmt.Sprintf("IPv4Option(%v:%v)", i.OptionType, i.OptionData)
|
||||
}
|
||||
|
||||
// for the current ipv4 options, return the number of bytes (including
|
||||
// padding that the options used)
|
||||
func (ip *IPv4) getIPv4OptionSize() uint8 {
|
||||
optionSize := uint8(0)
|
||||
for _, opt := range ip.Options {
|
||||
switch opt.OptionType {
|
||||
case 0:
|
||||
// this is the end of option lists
|
||||
optionSize++
|
||||
case 1:
|
||||
// this is the padding
|
||||
optionSize++
|
||||
default:
|
||||
optionSize += opt.OptionLength
|
||||
|
||||
}
|
||||
}
|
||||
// make sure the options are aligned to 32 bit boundary
|
||||
if (optionSize % 4) != 0 {
|
||||
optionSize += 4 - (optionSize % 4)
|
||||
}
|
||||
return optionSize
|
||||
}
|
||||
|
||||
// SerializeTo writes the serialized form of this layer into the
|
||||
// SerializationBuffer, implementing gopacket.SerializableLayer.
|
||||
func (ip *IPv4) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
|
||||
optionLength := ip.getIPv4OptionSize()
|
||||
bytes, err := b.PrependBytes(20 + int(optionLength))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if opts.FixLengths {
|
||||
ip.IHL = 5 + (optionLength / 4)
|
||||
ip.Length = uint16(len(b.Bytes()))
|
||||
}
|
||||
bytes[0] = (ip.Version << 4) | ip.IHL
|
||||
bytes[1] = ip.TOS
|
||||
binary.BigEndian.PutUint16(bytes[2:], ip.Length)
|
||||
binary.BigEndian.PutUint16(bytes[4:], ip.Id)
|
||||
binary.BigEndian.PutUint16(bytes[6:], ip.flagsfrags())
|
||||
bytes[8] = ip.TTL
|
||||
bytes[9] = byte(ip.Protocol)
|
||||
if err := ip.AddressTo4(); err != nil {
|
||||
return err
|
||||
}
|
||||
copy(bytes[12:16], ip.SrcIP)
|
||||
copy(bytes[16:20], ip.DstIP)
|
||||
|
||||
curLocation := 20
|
||||
// Now, we will encode the options
|
||||
for _, opt := range ip.Options {
|
||||
switch opt.OptionType {
|
||||
case 0:
|
||||
// this is the end of option lists
|
||||
bytes[curLocation] = 0
|
||||
curLocation++
|
||||
case 1:
|
||||
// this is the padding
|
||||
bytes[curLocation] = 1
|
||||
curLocation++
|
||||
default:
|
||||
bytes[curLocation] = opt.OptionType
|
||||
bytes[curLocation+1] = opt.OptionLength
|
||||
|
||||
// sanity checking to protect us from buffer overrun
|
||||
if len(opt.OptionData) > int(opt.OptionLength-2) {
|
||||
return errors.New("option length is smaller than length of option data")
|
||||
}
|
||||
copy(bytes[curLocation+2:curLocation+int(opt.OptionLength)], opt.OptionData)
|
||||
curLocation += int(opt.OptionLength)
|
||||
}
|
||||
}
|
||||
|
||||
if opts.ComputeChecksums {
|
||||
ip.Checksum = checksum(bytes)
|
||||
}
|
||||
binary.BigEndian.PutUint16(bytes[10:], ip.Checksum)
|
||||
return nil
|
||||
}
|
||||
|
||||
func checksum(bytes []byte) uint16 {
|
||||
// Clear checksum bytes
|
||||
bytes[10] = 0
|
||||
bytes[11] = 0
|
||||
|
||||
// Compute checksum
|
||||
var csum uint32
|
||||
for i := 0; i < len(bytes); i += 2 {
|
||||
csum += uint32(bytes[i]) << 8
|
||||
csum += uint32(bytes[i+1])
|
||||
}
|
||||
for {
|
||||
// Break when sum is less or equals to 0xFFFF
|
||||
if csum <= 65535 {
|
||||
break
|
||||
}
|
||||
// Add carry to the sum
|
||||
csum = (csum >> 16) + uint32(uint16(csum))
|
||||
}
|
||||
// Flip all the bits
|
||||
return ^uint16(csum)
|
||||
}
|
||||
|
||||
func (ip *IPv4) flagsfrags() (ff uint16) {
|
||||
ff |= uint16(ip.Flags) << 13
|
||||
ff |= ip.FragOffset
|
||||
return
|
||||
}
|
||||
|
||||
// DecodeFromBytes decodes the given bytes into this layer.
|
||||
func (ip *IPv4) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
|
||||
flagsfrags := binary.BigEndian.Uint16(data[6:8])
|
||||
|
||||
ip.Version = uint8(data[0]) >> 4
|
||||
ip.IHL = uint8(data[0]) & 0x0F
|
||||
ip.TOS = data[1]
|
||||
ip.Length = binary.BigEndian.Uint16(data[2:4])
|
||||
ip.Id = binary.BigEndian.Uint16(data[4:6])
|
||||
ip.Flags = IPv4Flag(flagsfrags >> 13)
|
||||
ip.FragOffset = flagsfrags & 0x1FFF
|
||||
ip.TTL = data[8]
|
||||
ip.Protocol = IPProtocol(data[9])
|
||||
ip.Checksum = binary.BigEndian.Uint16(data[10:12])
|
||||
ip.SrcIP = data[12:16]
|
||||
ip.DstIP = data[16:20]
|
||||
ip.Options = ip.Options[:0]
|
||||
// Set up an initial guess for contents/payload... we'll reset these soon.
|
||||
ip.BaseLayer = BaseLayer{Contents: data}
|
||||
|
||||
// This code is added for the following enviroment:
|
||||
// * Windows 10 with TSO option activated. ( tested on Hyper-V, RealTek ethernet driver )
|
||||
if ip.Length == 0 {
|
||||
// If using TSO(TCP Segmentation Offload), length is zero.
|
||||
// The actual packet length is the length of data.
|
||||
ip.Length = uint16(len(data))
|
||||
}
|
||||
|
||||
if ip.Length < 20 {
|
||||
return fmt.Errorf("Invalid (too small) IP length (%d < 20)", ip.Length)
|
||||
} else if ip.IHL < 5 {
|
||||
return fmt.Errorf("Invalid (too small) IP header length (%d < 5)", ip.IHL)
|
||||
} else if int(ip.IHL*4) > int(ip.Length) {
|
||||
return fmt.Errorf("Invalid IP header length > IP length (%d > %d)", ip.IHL, ip.Length)
|
||||
}
|
||||
if cmp := len(data) - int(ip.Length); cmp > 0 {
|
||||
data = data[:ip.Length]
|
||||
} else if cmp < 0 {
|
||||
df.SetTruncated()
|
||||
if int(ip.IHL)*4 > len(data) {
|
||||
return errors.New("Not all IP header bytes available")
|
||||
}
|
||||
}
|
||||
ip.Contents = data[:ip.IHL*4]
|
||||
ip.Payload = data[ip.IHL*4:]
|
||||
// From here on, data contains the header options.
|
||||
data = data[20 : ip.IHL*4]
|
||||
// Pull out IP options
|
||||
for len(data) > 0 {
|
||||
if ip.Options == nil {
|
||||
// Pre-allocate to avoid growing the slice too much.
|
||||
ip.Options = make([]IPv4Option, 0, 4)
|
||||
}
|
||||
opt := IPv4Option{OptionType: data[0]}
|
||||
switch opt.OptionType {
|
||||
case 0: // End of options
|
||||
opt.OptionLength = 1
|
||||
ip.Options = append(ip.Options, opt)
|
||||
ip.Padding = data[1:]
|
||||
break
|
||||
case 1: // 1 byte padding
|
||||
opt.OptionLength = 1
|
||||
default:
|
||||
opt.OptionLength = data[1]
|
||||
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)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (i *IPv4) CanDecode() gopacket.LayerClass {
|
||||
return LayerTypeIPv4
|
||||
}
|
||||
|
||||
func (i *IPv4) NextLayerType() gopacket.LayerType {
|
||||
if i.Flags&IPv4MoreFragments != 0 || i.FragOffset != 0 {
|
||||
return gopacket.LayerTypeFragment
|
||||
}
|
||||
return i.Protocol.LayerType()
|
||||
}
|
||||
|
||||
func decodeIPv4(data []byte, p gopacket.PacketBuilder) error {
|
||||
ip := &IPv4{}
|
||||
err := ip.DecodeFromBytes(data, p)
|
||||
p.AddLayer(ip)
|
||||
p.SetNetworkLayer(ip)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return p.NextDecoder(ip.NextLayerType())
|
||||
}
|
||||
|
||||
func checkIPv4Address(addr net.IP) (net.IP, error) {
|
||||
if c := addr.To4(); c != nil {
|
||||
return c, nil
|
||||
}
|
||||
if len(addr) == net.IPv6len {
|
||||
return nil, errors.New("address is IPv6")
|
||||
}
|
||||
return nil, fmt.Errorf("wrong length of %d bytes instead of %d", len(addr), net.IPv4len)
|
||||
}
|
||||
|
||||
func (ip *IPv4) AddressTo4() error {
|
||||
var src, dst net.IP
|
||||
|
||||
if addr, err := checkIPv4Address(ip.SrcIP); err != nil {
|
||||
return fmt.Errorf("Invalid source IPv4 address (%s)", err)
|
||||
} else {
|
||||
src = addr
|
||||
}
|
||||
if addr, err := checkIPv4Address(ip.DstIP); err != nil {
|
||||
return fmt.Errorf("Invalid destination IPv4 address (%s)", err)
|
||||
} else {
|
||||
dst = addr
|
||||
}
|
||||
ip.SrcIP = src
|
||||
ip.DstIP = dst
|
||||
return nil
|
||||
}
|
@ -0,0 +1,650 @@
|
||||
// Copyright 2012 Google, Inc. All rights reserved.
|
||||
// Copyright 2009-2011 Andreas Krennmair. 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"
|
||||
"fmt"
|
||||
"net"
|
||||
|
||||
"github.com/google/gopacket"
|
||||
)
|
||||
|
||||
const (
|
||||
IPv6HopByHopOptionJumbogram = 0xC2 // RFC 2675
|
||||
)
|
||||
|
||||
const (
|
||||
ipv6MaxPayloadLength = 65535
|
||||
)
|
||||
|
||||
// IPv6 is the layer for the IPv6 header.
|
||||
type IPv6 struct {
|
||||
// http://www.networksorcery.com/enp/protocol/ipv6.htm
|
||||
BaseLayer
|
||||
Version uint8
|
||||
TrafficClass uint8
|
||||
FlowLabel uint32
|
||||
Length uint16
|
||||
NextHeader IPProtocol
|
||||
HopLimit uint8
|
||||
SrcIP net.IP
|
||||
DstIP net.IP
|
||||
HopByHop *IPv6HopByHop
|
||||
// hbh will be pointed to by HopByHop if that layer exists.
|
||||
hbh IPv6HopByHop
|
||||
}
|
||||
|
||||
// LayerType returns LayerTypeIPv6
|
||||
func (i *IPv6) LayerType() gopacket.LayerType { return LayerTypeIPv6 }
|
||||
|
||||
func (i *IPv6) NetworkFlow() gopacket.Flow {
|
||||
return gopacket.NewFlow(EndpointIPv6, i.SrcIP, i.DstIP)
|
||||
}
|
||||
|
||||
// Search for Jumbo Payload TLV in IPv6HopByHop and return (length, true) if found
|
||||
func getIPv6HopByHopJumboLength(hopopts *IPv6HopByHop) (uint32, bool, error) {
|
||||
var tlv *IPv6HopByHopOption
|
||||
|
||||
for _, t := range hopopts.Options {
|
||||
if t.OptionType == IPv6HopByHopOptionJumbogram {
|
||||
tlv = t
|
||||
break
|
||||
}
|
||||
}
|
||||
if tlv == nil {
|
||||
// Not found
|
||||
return 0, false, nil
|
||||
}
|
||||
if len(tlv.OptionData) != 4 {
|
||||
return 0, false, errors.New("Jumbo length TLV data must have length 4")
|
||||
}
|
||||
l := binary.BigEndian.Uint32(tlv.OptionData)
|
||||
if l <= ipv6MaxPayloadLength {
|
||||
return 0, false, fmt.Errorf("Jumbo length cannot be less than %d", ipv6MaxPayloadLength+1)
|
||||
}
|
||||
// Found
|
||||
return l, true, nil
|
||||
}
|
||||
|
||||
// Adds zero-valued Jumbo TLV to IPv6 header if it does not exist
|
||||
// (if necessary add hop-by-hop header)
|
||||
func addIPv6JumboOption(ip6 *IPv6) {
|
||||
var tlv *IPv6HopByHopOption
|
||||
|
||||
if ip6.HopByHop == nil {
|
||||
// Add IPv6 HopByHop
|
||||
ip6.HopByHop = &IPv6HopByHop{}
|
||||
ip6.HopByHop.NextHeader = ip6.NextHeader
|
||||
ip6.HopByHop.HeaderLength = 0
|
||||
ip6.NextHeader = IPProtocolIPv6HopByHop
|
||||
}
|
||||
for _, t := range ip6.HopByHop.Options {
|
||||
if t.OptionType == IPv6HopByHopOptionJumbogram {
|
||||
tlv = t
|
||||
break
|
||||
}
|
||||
}
|
||||
if tlv == nil {
|
||||
// Add Jumbo TLV
|
||||
tlv = &IPv6HopByHopOption{}
|
||||
ip6.HopByHop.Options = append(ip6.HopByHop.Options, tlv)
|
||||
}
|
||||
tlv.SetJumboLength(0)
|
||||
}
|
||||
|
||||
// Set jumbo length in serialized IPv6 payload (starting with HopByHop header)
|
||||
func setIPv6PayloadJumboLength(hbh []byte) error {
|
||||
pLen := len(hbh)
|
||||
if pLen < 8 {
|
||||
//HopByHop is minimum 8 bytes
|
||||
return fmt.Errorf("Invalid IPv6 payload (length %d)", pLen)
|
||||
}
|
||||
hbhLen := int((hbh[1] + 1) * 8)
|
||||
if hbhLen > pLen {
|
||||
return fmt.Errorf("Invalid hop-by-hop length (length: %d, payload: %d", hbhLen, pLen)
|
||||
}
|
||||
offset := 2 //start with options
|
||||
for offset < hbhLen {
|
||||
opt := hbh[offset]
|
||||
if opt == 0 {
|
||||
//Pad1
|
||||
offset += 1
|
||||
continue
|
||||
}
|
||||
optLen := int(hbh[offset+1])
|
||||
if opt == IPv6HopByHopOptionJumbogram {
|
||||
if optLen == 4 {
|
||||
binary.BigEndian.PutUint32(hbh[offset+2:], uint32(pLen))
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("Jumbo TLV too short (%d bytes)", optLen)
|
||||
}
|
||||
offset += 2 + optLen
|
||||
}
|
||||
return errors.New("Jumbo TLV not found")
|
||||
}
|
||||
|
||||
// SerializeTo writes the serialized form of this layer into the
|
||||
// SerializationBuffer, implementing gopacket.SerializableLayer.
|
||||
// See the docs for gopacket.SerializableLayer for more info.
|
||||
func (ip6 *IPv6) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
|
||||
var jumbo bool
|
||||
var err error
|
||||
|
||||
payload := b.Bytes()
|
||||
pLen := len(payload)
|
||||
if pLen > ipv6MaxPayloadLength {
|
||||
jumbo = true
|
||||
if opts.FixLengths {
|
||||
// We need to set the length later because the hop-by-hop header may
|
||||
// not exist or else need padding, so pLen may yet change
|
||||
addIPv6JumboOption(ip6)
|
||||
} else if ip6.HopByHop == nil {
|
||||
return fmt.Errorf("Cannot fit payload length of %d into IPv6 packet", pLen)
|
||||
} else {
|
||||
_, ok, err := getIPv6HopByHopJumboLength(ip6.HopByHop)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !ok {
|
||||
return errors.New("Missing jumbo length hop-by-hop option")
|
||||
}
|
||||
}
|
||||
}
|
||||
if ip6.HopByHop != nil {
|
||||
if ip6.NextHeader != IPProtocolIPv6HopByHop {
|
||||
// Just fix it instead of throwing an error
|
||||
ip6.NextHeader = IPProtocolIPv6HopByHop
|
||||
}
|
||||
err = ip6.HopByHop.SerializeTo(b, opts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
payload = b.Bytes()
|
||||
pLen = len(payload)
|
||||
if opts.FixLengths && jumbo {
|
||||
err := setIPv6PayloadJumboLength(payload)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
if !jumbo && pLen > ipv6MaxPayloadLength {
|
||||
return errors.New("Cannot fit payload into IPv6 header")
|
||||
}
|
||||
bytes, err := b.PrependBytes(40)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
bytes[0] = (ip6.Version << 4) | (ip6.TrafficClass >> 4)
|
||||
bytes[1] = (ip6.TrafficClass << 4) | uint8(ip6.FlowLabel>>16)
|
||||
binary.BigEndian.PutUint16(bytes[2:], uint16(ip6.FlowLabel))
|
||||
if opts.FixLengths {
|
||||
if jumbo {
|
||||
ip6.Length = 0
|
||||
} else {
|
||||
ip6.Length = uint16(pLen)
|
||||
}
|
||||
}
|
||||
binary.BigEndian.PutUint16(bytes[4:], ip6.Length)
|
||||
bytes[6] = byte(ip6.NextHeader)
|
||||
bytes[7] = byte(ip6.HopLimit)
|
||||
if err := ip6.AddressTo16(); err != nil {
|
||||
return err
|
||||
}
|
||||
copy(bytes[8:], ip6.SrcIP)
|
||||
copy(bytes[24:], ip6.DstIP)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ip6 *IPv6) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
|
||||
ip6.Version = uint8(data[0]) >> 4
|
||||
ip6.TrafficClass = uint8((binary.BigEndian.Uint16(data[0:2]) >> 4) & 0x00FF)
|
||||
ip6.FlowLabel = binary.BigEndian.Uint32(data[0:4]) & 0x000FFFFF
|
||||
ip6.Length = binary.BigEndian.Uint16(data[4:6])
|
||||
ip6.NextHeader = IPProtocol(data[6])
|
||||
ip6.HopLimit = data[7]
|
||||
ip6.SrcIP = data[8:24]
|
||||
ip6.DstIP = data[24:40]
|
||||
ip6.HopByHop = nil
|
||||
ip6.BaseLayer = BaseLayer{data[:40], data[40:]}
|
||||
|
||||
// We treat a HopByHop IPv6 option as part of the IPv6 packet, since its
|
||||
// options are crucial for understanding what's actually happening per packet.
|
||||
if ip6.NextHeader == IPProtocolIPv6HopByHop {
|
||||
err := ip6.hbh.DecodeFromBytes(ip6.Payload, df)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ip6.HopByHop = &ip6.hbh
|
||||
pEnd, jumbo, err := getIPv6HopByHopJumboLength(ip6.HopByHop)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if jumbo && ip6.Length == 0 {
|
||||
pEnd := int(pEnd)
|
||||
if pEnd > len(ip6.Payload) {
|
||||
df.SetTruncated()
|
||||
pEnd = len(ip6.Payload)
|
||||
}
|
||||
ip6.Payload = ip6.Payload[:pEnd]
|
||||
return nil
|
||||
} else if jumbo && ip6.Length != 0 {
|
||||
return errors.New("IPv6 has jumbo length and IPv6 length is not 0")
|
||||
} else if !jumbo && ip6.Length == 0 {
|
||||
return errors.New("IPv6 length 0, but HopByHop header does not have jumbogram option")
|
||||
}
|
||||
}
|
||||
|
||||
if ip6.Length == 0 {
|
||||
return fmt.Errorf("IPv6 length 0, but next header is %v, not HopByHop", ip6.NextHeader)
|
||||
} else {
|
||||
pEnd := int(ip6.Length)
|
||||
if pEnd > len(ip6.Payload) {
|
||||
df.SetTruncated()
|
||||
pEnd = len(ip6.Payload)
|
||||
}
|
||||
ip6.Payload = ip6.Payload[:pEnd]
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (i *IPv6) CanDecode() gopacket.LayerClass {
|
||||
return LayerTypeIPv6
|
||||
}
|
||||
|
||||
func (i *IPv6) NextLayerType() gopacket.LayerType {
|
||||
if i.HopByHop != nil {
|
||||
return i.HopByHop.NextHeader.LayerType()
|
||||
}
|
||||
return i.NextHeader.LayerType()
|
||||
}
|
||||
|
||||
func decodeIPv6(data []byte, p gopacket.PacketBuilder) error {
|
||||
ip6 := &IPv6{}
|
||||
err := ip6.DecodeFromBytes(data, p)
|
||||
p.AddLayer(ip6)
|
||||
p.SetNetworkLayer(ip6)
|
||||
if ip6.HopByHop != nil {
|
||||
p.AddLayer(ip6.HopByHop)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return p.NextDecoder(ip6.NextLayerType())
|
||||
}
|
||||
|
||||
type ipv6HeaderTLVOption struct {
|
||||
OptionType, OptionLength uint8
|
||||
ActualLength int
|
||||
OptionData []byte
|
||||
OptionAlignment [2]uint8 // Xn+Y = [2]uint8{X, Y}
|
||||
}
|
||||
|
||||
func (h *ipv6HeaderTLVOption) serializeTo(data []byte, fixLengths bool, dryrun bool) int {
|
||||
if fixLengths {
|
||||
h.OptionLength = uint8(len(h.OptionData))
|
||||
}
|
||||
length := int(h.OptionLength) + 2
|
||||
if !dryrun {
|
||||
data[0] = h.OptionType
|
||||
data[1] = h.OptionLength
|
||||
copy(data[2:], h.OptionData)
|
||||
}
|
||||
return length
|
||||
}
|
||||
|
||||
func decodeIPv6HeaderTLVOption(data []byte) (h *ipv6HeaderTLVOption) {
|
||||
h = &ipv6HeaderTLVOption{}
|
||||
if data[0] == 0 {
|
||||
h.ActualLength = 1
|
||||
return
|
||||
}
|
||||
h.OptionType = data[0]
|
||||
h.OptionLength = data[1]
|
||||
h.ActualLength = int(h.OptionLength) + 2
|
||||
h.OptionData = data[2:h.ActualLength]
|
||||
return
|
||||
}
|
||||
|
||||
func serializeTLVOptionPadding(data []byte, padLength int) {
|
||||
if padLength <= 0 {
|
||||
return
|
||||
}
|
||||
if padLength == 1 {
|
||||
data[0] = 0x0
|
||||
return
|
||||
}
|
||||
tlvLength := uint8(padLength) - 2
|
||||
data[0] = 0x1
|
||||
data[1] = tlvLength
|
||||
if tlvLength != 0 {
|
||||
for k := range data[2:] {
|
||||
data[k+2] = 0x0
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// If buf is 'nil' do a serialize dry run
|
||||
func serializeIPv6HeaderTLVOptions(buf []byte, options []*ipv6HeaderTLVOption, fixLengths bool) int {
|
||||
var l int
|
||||
|
||||
dryrun := buf == nil
|
||||
length := 2
|
||||
for _, opt := range options {
|
||||
if fixLengths {
|
||||
x := int(opt.OptionAlignment[0])
|
||||
y := int(opt.OptionAlignment[1])
|
||||
if x != 0 {
|
||||
n := length / x
|
||||
offset := x*n + y
|
||||
if offset < length {
|
||||
offset += x
|
||||
}
|
||||
if length != offset {
|
||||
pad := offset - length
|
||||
if !dryrun {
|
||||
serializeTLVOptionPadding(buf[length-2:], pad)
|
||||
}
|
||||
length += pad
|
||||
}
|
||||
}
|
||||
}
|
||||
if dryrun {
|
||||
l = opt.serializeTo(nil, fixLengths, true)
|
||||
} else {
|
||||
l = opt.serializeTo(buf[length-2:], fixLengths, false)
|
||||
}
|
||||
length += l
|
||||
}
|
||||
if fixLengths {
|
||||
pad := length % 8
|
||||
if pad != 0 {
|
||||
if !dryrun {
|
||||
serializeTLVOptionPadding(buf[length-2:], pad)
|
||||
}
|
||||
length += pad
|
||||
}
|
||||
}
|
||||
return length - 2
|
||||
}
|
||||
|
||||
type ipv6ExtensionBase struct {
|
||||
BaseLayer
|
||||
NextHeader IPProtocol
|
||||
HeaderLength uint8
|
||||
ActualLength int
|
||||
}
|
||||
|
||||
func decodeIPv6ExtensionBase(data []byte) (i ipv6ExtensionBase) {
|
||||
i.NextHeader = IPProtocol(data[0])
|
||||
i.HeaderLength = data[1]
|
||||
i.ActualLength = int(i.HeaderLength)*8 + 8
|
||||
i.Contents = data[:i.ActualLength]
|
||||
i.Payload = data[i.ActualLength:]
|
||||
return
|
||||
}
|
||||
|
||||
// IPv6ExtensionSkipper is a DecodingLayer which decodes and ignores v6
|
||||
// extensions. You can use it with a DecodingLayerParser to handle IPv6 stacks
|
||||
// which may or may not have extensions.
|
||||
type IPv6ExtensionSkipper struct {
|
||||
NextHeader IPProtocol
|
||||
BaseLayer
|
||||
}
|
||||
|
||||
func (i *IPv6ExtensionSkipper) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
|
||||
extension := decodeIPv6ExtensionBase(data)
|
||||
i.BaseLayer = BaseLayer{data[:extension.ActualLength], data[extension.ActualLength:]}
|
||||
i.NextHeader = extension.NextHeader
|
||||
return nil
|
||||
}
|
||||
|
||||
func (i *IPv6ExtensionSkipper) CanDecode() gopacket.LayerClass {
|
||||
return LayerClassIPv6Extension
|
||||
}
|
||||
|
||||
func (i *IPv6ExtensionSkipper) NextLayerType() gopacket.LayerType {
|
||||
return i.NextHeader.LayerType()
|
||||
}
|
||||
|
||||
// IPv6HopByHopOption is a TLV option present in an IPv6 hop-by-hop extension.
|
||||
type IPv6HopByHopOption ipv6HeaderTLVOption
|
||||
|
||||
// IPv6HopByHop is the IPv6 hop-by-hop extension.
|
||||
type IPv6HopByHop struct {
|
||||
ipv6ExtensionBase
|
||||
Options []*IPv6HopByHopOption
|
||||
}
|
||||
|
||||
// LayerType returns LayerTypeIPv6HopByHop.
|
||||
func (i *IPv6HopByHop) LayerType() gopacket.LayerType { return LayerTypeIPv6HopByHop }
|
||||
|
||||
func (i *IPv6HopByHop) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
|
||||
var bytes []byte
|
||||
var err error
|
||||
|
||||
o := make([]*ipv6HeaderTLVOption, 0, len(i.Options))
|
||||
for _, v := range i.Options {
|
||||
o = append(o, (*ipv6HeaderTLVOption)(v))
|
||||
}
|
||||
|
||||
l := serializeIPv6HeaderTLVOptions(nil, o, opts.FixLengths)
|
||||
bytes, err = b.PrependBytes(l)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
serializeIPv6HeaderTLVOptions(bytes, o, opts.FixLengths)
|
||||
|
||||
length := len(bytes) + 2
|
||||
if length%8 != 0 {
|
||||
return errors.New("IPv6HopByHop actual length must be multiple of 8")
|
||||
}
|
||||
bytes, err = b.PrependBytes(2)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
bytes[0] = uint8(i.NextHeader)
|
||||
if opts.FixLengths {
|
||||
i.HeaderLength = uint8((length / 8) - 1)
|
||||
}
|
||||
bytes[1] = uint8(i.HeaderLength)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (i *IPv6HopByHop) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
|
||||
i.ipv6ExtensionBase = decodeIPv6ExtensionBase(data)
|
||||
offset := 2
|
||||
for offset < i.ActualLength {
|
||||
opt := decodeIPv6HeaderTLVOption(data[offset:])
|
||||
i.Options = append(i.Options, (*IPv6HopByHopOption)(opt))
|
||||
offset += opt.ActualLength
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func decodeIPv6HopByHop(data []byte, p gopacket.PacketBuilder) error {
|
||||
i := &IPv6HopByHop{}
|
||||
err := i.DecodeFromBytes(data, p)
|
||||
p.AddLayer(i)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return p.NextDecoder(i.NextHeader)
|
||||
}
|
||||
|
||||
func (o *IPv6HopByHopOption) SetJumboLength(len uint32) {
|
||||
o.OptionType = IPv6HopByHopOptionJumbogram
|
||||
o.OptionLength = 4
|
||||
o.ActualLength = 6
|
||||
if o.OptionData == nil {
|
||||
o.OptionData = make([]byte, 4)
|
||||
}
|
||||
binary.BigEndian.PutUint32(o.OptionData, len)
|
||||
o.OptionAlignment = [2]uint8{4, 2}
|
||||
}
|
||||
|
||||
// IPv6Routing is the IPv6 routing extension.
|
||||
type IPv6Routing struct {
|
||||
ipv6ExtensionBase
|
||||
RoutingType uint8
|
||||
SegmentsLeft uint8
|
||||
// This segment is supposed to be zero according to RFC2460, the second set of
|
||||
// 4 bytes in the extension.
|
||||
Reserved []byte
|
||||
// SourceRoutingIPs is the set of IPv6 addresses requested for source routing,
|
||||
// set only if RoutingType == 0.
|
||||
SourceRoutingIPs []net.IP
|
||||
}
|
||||
|
||||
// LayerType returns LayerTypeIPv6Routing.
|
||||
func (i *IPv6Routing) LayerType() gopacket.LayerType { return LayerTypeIPv6Routing }
|
||||
|
||||
func decodeIPv6Routing(data []byte, p gopacket.PacketBuilder) error {
|
||||
i := &IPv6Routing{
|
||||
ipv6ExtensionBase: decodeIPv6ExtensionBase(data),
|
||||
RoutingType: data[2],
|
||||
SegmentsLeft: data[3],
|
||||
Reserved: data[4:8],
|
||||
}
|
||||
switch i.RoutingType {
|
||||
case 0: // Source routing
|
||||
if (i.ActualLength-8)%16 != 0 {
|
||||
return fmt.Errorf("Invalid IPv6 source routing, length of type 0 packet %d", i.ActualLength)
|
||||
}
|
||||
for d := i.Contents[8:]; len(d) >= 16; d = d[16:] {
|
||||
i.SourceRoutingIPs = append(i.SourceRoutingIPs, net.IP(d[:16]))
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf("Unknown IPv6 routing header type %d", i.RoutingType)
|
||||
}
|
||||
p.AddLayer(i)
|
||||
return p.NextDecoder(i.NextHeader)
|
||||
}
|
||||
|
||||
// IPv6Fragment is the IPv6 fragment header, used for packet
|
||||
// fragmentation/defragmentation.
|
||||
type IPv6Fragment struct {
|
||||
BaseLayer
|
||||
NextHeader IPProtocol
|
||||
// Reserved1 is bits [8-16), from least to most significant, 0-indexed
|
||||
Reserved1 uint8
|
||||
FragmentOffset uint16
|
||||
// Reserved2 is bits [29-31), from least to most significant, 0-indexed
|
||||
Reserved2 uint8
|
||||
MoreFragments bool
|
||||
Identification uint32
|
||||
}
|
||||
|
||||
// LayerType returns LayerTypeIPv6Fragment.
|
||||
func (i *IPv6Fragment) LayerType() gopacket.LayerType { return LayerTypeIPv6Fragment }
|
||||
|
||||
func decodeIPv6Fragment(data []byte, p gopacket.PacketBuilder) error {
|
||||
i := &IPv6Fragment{
|
||||
BaseLayer: BaseLayer{data[:8], data[8:]},
|
||||
NextHeader: IPProtocol(data[0]),
|
||||
Reserved1: data[1],
|
||||
FragmentOffset: binary.BigEndian.Uint16(data[2:4]) >> 3,
|
||||
Reserved2: data[3] & 0x6 >> 1,
|
||||
MoreFragments: data[3]&0x1 != 0,
|
||||
Identification: binary.BigEndian.Uint32(data[4:8]),
|
||||
}
|
||||
p.AddLayer(i)
|
||||
return p.NextDecoder(gopacket.DecodeFragment)
|
||||
}
|
||||
|
||||
// IPv6DestinationOption is a TLV option present in an IPv6 destination options extension.
|
||||
type IPv6DestinationOption ipv6HeaderTLVOption
|
||||
|
||||
// IPv6Destination is the IPv6 destination options header.
|
||||
type IPv6Destination struct {
|
||||
ipv6ExtensionBase
|
||||
Options []*IPv6DestinationOption
|
||||
}
|
||||
|
||||
// LayerType returns LayerTypeIPv6Destination.
|
||||
func (i *IPv6Destination) LayerType() gopacket.LayerType { return LayerTypeIPv6Destination }
|
||||
|
||||
func (i *IPv6Destination) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
|
||||
i.ipv6ExtensionBase = decodeIPv6ExtensionBase(data)
|
||||
offset := 2
|
||||
for offset < i.ActualLength {
|
||||
opt := decodeIPv6HeaderTLVOption(data[offset:])
|
||||
i.Options = append(i.Options, (*IPv6DestinationOption)(opt))
|
||||
offset += opt.ActualLength
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func decodeIPv6Destination(data []byte, p gopacket.PacketBuilder) error {
|
||||
i := &IPv6Destination{}
|
||||
err := i.DecodeFromBytes(data, p)
|
||||
p.AddLayer(i)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return p.NextDecoder(i.NextHeader)
|
||||
}
|
||||
|
||||
// SerializeTo writes the serialized form of this layer into the
|
||||
// SerializationBuffer, implementing gopacket.SerializableLayer.
|
||||
// See the docs for gopacket.SerializableLayer for more info.
|
||||
func (i *IPv6Destination) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
|
||||
var bytes []byte
|
||||
var err error
|
||||
|
||||
o := make([]*ipv6HeaderTLVOption, 0, len(i.Options))
|
||||
for _, v := range i.Options {
|
||||
o = append(o, (*ipv6HeaderTLVOption)(v))
|
||||
}
|
||||
|
||||
l := serializeIPv6HeaderTLVOptions(nil, o, opts.FixLengths)
|
||||
bytes, err = b.PrependBytes(l)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
serializeIPv6HeaderTLVOptions(bytes, o, opts.FixLengths)
|
||||
|
||||
length := len(bytes) + 2
|
||||
if length%8 != 0 {
|
||||
return errors.New("IPv6Destination actual length must be multiple of 8")
|
||||
}
|
||||
bytes, err = b.PrependBytes(2)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
bytes[0] = uint8(i.NextHeader)
|
||||
if opts.FixLengths {
|
||||
i.HeaderLength = uint8((length / 8) - 1)
|
||||
}
|
||||
bytes[1] = uint8(i.HeaderLength)
|
||||
return nil
|
||||
}
|
||||
|
||||
func checkIPv6Address(addr net.IP) error {
|
||||
if len(addr) == net.IPv6len {
|
||||
return nil
|
||||
}
|
||||
if len(addr) == net.IPv4len {
|
||||
return errors.New("address is IPv4")
|
||||
}
|
||||
return fmt.Errorf("wrong length of %d bytes instead of %d", len(addr), net.IPv6len)
|
||||
}
|
||||
|
||||
func (ip *IPv6) AddressTo16() error {
|
||||
if err := checkIPv6Address(ip.SrcIP); err != nil {
|
||||
return fmt.Errorf("Invalid source IPv6 address (%s)", err)
|
||||
}
|
||||
if err := checkIPv6Address(ip.DstIP); err != nil {
|
||||
return fmt.Errorf("Invalid destination IPv6 address (%s)", err)
|
||||
}
|
||||
return nil
|
||||
}
|
@ -0,0 +1,68 @@
|
||||
// Copyright 2012 Google, Inc. All rights reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the LICENSE file in the root of the source
|
||||
// tree.
|
||||
|
||||
package layers
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"github.com/google/gopacket"
|
||||
)
|
||||
|
||||
// IPSecAH is the authentication header for IPv4/6 defined in
|
||||
// http://tools.ietf.org/html/rfc2402
|
||||
type IPSecAH struct {
|
||||
// While the auth header can be used for both IPv4 and v6, its format is that of
|
||||
// an IPv6 extension (NextHeader, PayloadLength, etc...), so we use ipv6ExtensionBase
|
||||
// to build it.
|
||||
ipv6ExtensionBase
|
||||
Reserved uint16
|
||||
SPI, Seq uint32
|
||||
AuthenticationData []byte
|
||||
}
|
||||
|
||||
// LayerType returns LayerTypeIPSecAH.
|
||||
func (i *IPSecAH) LayerType() gopacket.LayerType { return LayerTypeIPSecAH }
|
||||
|
||||
func decodeIPSecAH(data []byte, p gopacket.PacketBuilder) error {
|
||||
i := &IPSecAH{
|
||||
ipv6ExtensionBase: ipv6ExtensionBase{
|
||||
NextHeader: IPProtocol(data[0]),
|
||||
HeaderLength: data[1],
|
||||
},
|
||||
Reserved: binary.BigEndian.Uint16(data[2:4]),
|
||||
SPI: binary.BigEndian.Uint32(data[4:8]),
|
||||
Seq: binary.BigEndian.Uint32(data[8:12]),
|
||||
}
|
||||
i.ActualLength = (int(i.HeaderLength) + 2) * 4
|
||||
i.AuthenticationData = data[12:i.ActualLength]
|
||||
i.Contents = data[:i.ActualLength]
|
||||
i.Payload = data[i.ActualLength:]
|
||||
p.AddLayer(i)
|
||||
return p.NextDecoder(i.NextHeader)
|
||||
}
|
||||
|
||||
// IPSecESP is the encapsulating security payload defined in
|
||||
// http://tools.ietf.org/html/rfc2406
|
||||
type IPSecESP struct {
|
||||
BaseLayer
|
||||
SPI, Seq uint32
|
||||
// Encrypted contains the encrypted set of bytes sent in an ESP
|
||||
Encrypted []byte
|
||||
}
|
||||
|
||||
// LayerType returns LayerTypeIPSecESP.
|
||||
func (i *IPSecESP) LayerType() gopacket.LayerType { return LayerTypeIPSecESP }
|
||||
|
||||
func decodeIPSecESP(data []byte, p gopacket.PacketBuilder) error {
|
||||
i := &IPSecESP{
|
||||
BaseLayer: BaseLayer{data, nil},
|
||||
SPI: binary.BigEndian.Uint32(data[:4]),
|
||||
Seq: binary.BigEndian.Uint32(data[4:8]),
|
||||
Encrypted: data[8:],
|
||||