Compare commits

...

24 Commits

Author SHA1 Message Date
Nuno Diegues 4b75943d59 Release 2022.9.0 2022-09-07 07:58:04 +01:00
cthuang fc20a22685 TUN-6695: Implement ICMP proxy for linux 2022-09-05 14:49:42 +00:00
cthuang faa86ffeca TUN-6737: Fix datagramV2Type should be declared in its own block so it starts at 0 2022-09-05 15:09:53 +01:00
Devin Carr f7a14d9200 TUN-6728: Verify http status code ingress rule 2022-09-02 09:14:03 -07:00
Nuno Diegues 902e5beb4f TUN-6729: Fix flaky TestClosePreviousProxies
I can only reproduce the flakiness, which is the hello world still
responding when it should be shut down already, in Windows (both in
TeamCity as well as my local VM). Locally, it only happens when the
machine is under high load.

Anyway, it's valid that the proxies take some time to shut down since
they handle that via channels asynchronously with regards to the event
that updates the configuration.
Hence, nothing is wrong, as long as they eventually shut down, which the
test still verifies.
2022-09-01 21:32:59 +00:00
Nuno Diegues 7ca5f7569a TUN-6726: Fix maxDatagramPayloadSize for Windows QUIC datagrams 2022-09-01 21:32:59 +00:00
Nuno Diegues 4ac68711cd TUN-6725: Fix testProxySSEAllData
This test was failing on Windows. We did not catch it before because our
TeamCity Windows builds were ignoring failed unit tests: TUN-6727

 - the fix is implementing WriteString for mockSSERespWriter
 - reason is because cfio.Copy was calling that, and not Write method,
   thus not triggering the usage of the channel for the test to continue
 - mockSSERespWriter was providing a valid implementation of WriteString
   via ResponseRecorder, which it implements via the embedded mockHTTPRespWriter
 - it is not clear why this only happened on Windows
 - changed it to be a top-level test since it did not share any code
   with other sub-tests in the same top-level test
2022-09-01 21:32:59 +00:00
Devin Carr 075ac1acf1 Release 2022.8.4 2022-08-31 15:19:40 -07:00
Devin Carr cfef0e737f TUN-6720: Remove forcibly closing connection during reconnect signal
Previously allowing the reconnect signal forcibly close the connection
caused a race condition on which error was returned by the errgroup
in the tunnel connection. Allowing the signal to return and provide
a context cancel to the connection provides a safer shutdown of the
tunnel for this test-only scenario.
2022-08-31 21:50:02 +00:00
Devin Carr 8ec0f7746b Release 2022.8.3 2022-08-31 20:54:54 +00:00
cthuang 2b3707e2b9 TUN-6717: Update Github action to run with Go 1.19 2022-08-31 12:22:57 +01:00
cthuang 7e760f9fcc TUN-6586: Change ICMP proxy to only build for Darwin and use echo ID to track flows 2022-08-27 22:37:08 +01:00
cthuang efb99d90d7 TUN-6708: Fix replace flow logic 2022-08-26 17:52:06 +01:00
João Oliveirinha e131125558 TUN-6699: Add metric for packet too big dropped 2022-08-26 16:02:43 +00:00
Devin Carr af6bf5c4e5 TUN-6704: Honor protocol flag when edge discovery is unreachable 2022-08-26 15:31:19 +00:00
Sudarsan Reddy e3390fcb15 TUN-6705: Tunnel should retry connections forever
Protocolbackoff arrays now have Retryforever flag set to true to enable
cloudflared to keep trying to reconnect perpetually.
2022-08-26 08:27:15 +00:00
Devin Carr fc5749328d TUN-6691: Properly error check for net.ErrClosed
UDP session would check if the socket was closed before returning but the net.ErrClosed could be wrapped in another error.
2022-08-25 09:44:32 -07:00
cthuang 59f5b0df83 TUN-6530: Implement ICMPv4 proxy
This proxy uses unprivileged datagram-oriented endpoint and is shared by all quic connections
2022-08-24 17:33:03 +01:00
João Oliveirinha f6bd4aa039 TUN-6676: Add suport for trailers in http2 connections 2022-08-24 15:16:30 +00:00
cthuang d2bc15e224 TUN-6667: DatagramMuxerV2 provides a method to receive RawPacket 2022-08-24 14:56:08 +01:00
cthuang bad2e8e812 TUN-6666: Define packet package
This package defines IP and ICMP packet, decoders, encoder and flow
2022-08-24 11:36:57 +01:00
João Oliveirinha 20ed7557f9 TUN-6679: Allow client side of quic request to close body
In a previous commit, we fixed a bug where the client roundtrip code
could close the request body, which in fact would be the quic.Stream,
thus closing the write-side.
The way that was fixed, prevented the client roundtrip code from closing
also read-side (the body).

This fixes that, by allowing close to only close the read side, which
will guarantee that any subsquent will fail with an error or EOF it
occurred before the close.
2022-08-23 10:43:45 +01:00
Sudarsan Reddy 8e9e1d973e TUN-6657: Ask for Tunnel ID and Configuration on Bug Report 2022-08-16 17:07:54 +00:00
Devin Carr a97673e8b9 TUN-6575: Consume cf-trace-id from incoming http2 TCP requests 2022-08-16 15:30:44 +00:00
271 changed files with 51140 additions and 2923 deletions
+4
View File
@@ -15,6 +15,10 @@ Steps to reproduce the behavior:
2. Run '....'
3. See error
If it's an issue with Cloudflare Tunnel:
4. Tunnel ID :
5. cloudflared config:
**Expected behavior**
A clear and concise description of what you expected to happen.
+1 -1
View File
@@ -4,7 +4,7 @@ jobs:
check:
strategy:
matrix:
go-version: [1.17.x]
go-version: [1.19.x]
os: [ubuntu-latest, macos-latest, windows-latest]
runs-on: ${{ matrix.os }}
steps:
+4
View File
@@ -1,3 +1,7 @@
## 2022.9.0
### New Features
- cloudflared now rejects ingress rules with invalid http status codes for http_status.
## 2022.8.1
### New Features
- cloudflared now remembers if it connected to a certain protocol successfully. If it did, it does not fall back to a lower
+28
View File
@@ -1,3 +1,31 @@
2022.9.0
- 2022-09-05 TUN-6737: Fix datagramV2Type should be declared in its own block so it starts at 0
- 2022-09-01 TUN-6725: Fix testProxySSEAllData
- 2022-09-01 TUN-6726: Fix maxDatagramPayloadSize for Windows QUIC datagrams
- 2022-09-01 TUN-6729: Fix flaky TestClosePreviousProxies
- 2022-09-01 TUN-6728: Verify http status code ingress rule
- 2022-08-25 TUN-6695: Implement ICMP proxy for linux
2022.8.4
- 2022-08-31 TUN-6717: Update Github action to run with Go 1.19
- 2022-08-31 TUN-6720: Remove forcibly closing connection during reconnect signal
- 2022-08-29 Release 2022.8.3
2022.8.3
- 2022-08-26 TUN-6708: Fix replace flow logic
- 2022-08-25 TUN-6705: Tunnel should retry connections forever
- 2022-08-25 TUN-6704: Honor protocol flag when edge discovery is unreachable
- 2022-08-25 TUN-6699: Add metric for packet too big dropped
- 2022-08-24 TUN-6691: Properly error check for net.ErrClosed
- 2022-08-22 TUN-6679: Allow client side of quic request to close body
- 2022-08-22 TUN-6586: Change ICMP proxy to only build for Darwin and use echo ID to track flows
- 2022-08-18 TUN-6530: Implement ICMPv4 proxy
- 2022-08-17 TUN-6666: Define packet package
- 2022-08-17 TUN-6667: DatagramMuxerV2 provides a method to receive RawPacket
- 2022-08-16 TUN-6657: Ask for Tunnel ID and Configuration on Bug Report
- 2022-08-16 TUN-6676: Add suport for trailers in http2 connections
- 2022-08-11 TUN-6575: Consume cf-trace-id from incoming http2 TCP requests
2022.8.2
- 2022-08-16 TUN-6656: Docker for arm64 should not be deployed in an amd64 container
+1 -1
View File
@@ -375,7 +375,7 @@ func StartServer(
errC <- metrics.ServeMetrics(metricsListener, ctx.Done(), readinessServer, quickTunnelURL, orchestrator, log)
}()
reconnectCh := make(chan supervisor.ReconnectSignal, 1)
reconnectCh := make(chan supervisor.ReconnectSignal, c.Int("ha-connections"))
if c.IsSet("stdin-control") {
log.Info().Msg("Enabling control through stdin")
go stdinControl(reconnectCh, log)
+6 -11
View File
@@ -47,17 +47,12 @@ class TestReconnect:
cloudflared.stdin.flush()
def assert_reconnect(self, config, cloudflared, repeat):
wait_tunnel_ready(tunnel_url=config.get_url(), require_min_connections=self.default_ha_conns)
wait_tunnel_ready(tunnel_url=config.get_url(),
require_min_connections=self.default_ha_conns)
for _ in range(repeat):
for i in range(self.default_ha_conns):
for _ in range(self.default_ha_conns):
self.send_reconnect(cloudflared, self.default_reconnect_secs)
expect_connections = self.default_ha_conns-i-1
if expect_connections > 0:
# Don't check if tunnel returns 200 here because there is a race condition between wait_tunnel_ready
# retrying to get 200 response and reconnecting
wait_tunnel_ready(require_min_connections=expect_connections)
else:
check_tunnel_not_connected()
check_tunnel_not_connected()
sleep(self.default_reconnect_secs * 2)
wait_tunnel_ready(tunnel_url=config.get_url(), require_min_connections=self.default_ha_conns)
wait_tunnel_ready(tunnel_url=config.get_url(),
require_min_connections=self.default_ha_conns)
+5 -2
View File
@@ -15,6 +15,7 @@ from constants import METRICS_PORT, MAX_RETRIES, BACKOFF_SECS
LOGGER = logging.getLogger(__name__)
def select_platform(plat):
return pytest.mark.skipif(
platform.system() != plat, reason=f"Only runs on {plat}")
@@ -108,13 +109,15 @@ def _log_cloudflared_logs(cfd_logs):
LOGGER.warning(line)
@retry(stop_max_attempt_number=MAX_RETRIES * BACKOFF_SECS, wait_fixed=1000)
@retry(stop_max_attempt_number=MAX_RETRIES, wait_fixed=BACKOFF_SECS * 1000)
def check_tunnel_not_connected():
url = f'http://localhost:{METRICS_PORT}/ready'
try:
resp = requests.get(url, timeout=1)
resp = requests.get(url, timeout=BACKOFF_SECS)
assert resp.status_code == 503, f"Expect {url} returns 503, got {resp.status_code}"
assert resp.json()[
"readyConnections"] == 0, "Expected all connections to be terminated (pending reconnect)"
# cloudflared might already terminate
except requests.exceptions.ConnectionError as e:
LOGGER.warning(f"Failed to connect to {url}, error: {e}")
+21 -4
View File
@@ -24,9 +24,16 @@ const (
LogFieldConnIndex = "connIndex"
MaxGracePeriod = time.Minute * 3
MaxConcurrentStreams = math.MaxUint32
contentTypeHeader = "content-type"
sseContentType = "text/event-stream"
grpcContentType = "application/grpc"
)
var switchingProtocolText = fmt.Sprintf("%d %s", http.StatusSwitchingProtocols, http.StatusText(http.StatusSwitchingProtocols))
var (
switchingProtocolText = fmt.Sprintf("%d %s", http.StatusSwitchingProtocols, http.StatusText(http.StatusSwitchingProtocols))
flushableContentTypes = []string{sseContentType, grpcContentType}
)
type Orchestrator interface {
UpdateConfig(version int32, config []byte) *pogs.UpdateConfigurationResponse
@@ -174,6 +181,7 @@ func (h *HTTPResponseReadWriteAcker) AckConnection(tracePropagation string) erro
Status: switchingProtocolText,
StatusCode: http.StatusSwitchingProtocols,
ContentLength: -1,
Header: http.Header{},
}
if secWebsocketKey := h.req.Header.Get("Sec-WebSocket-Key"); secWebsocketKey != "" {
@@ -189,6 +197,7 @@ func (h *HTTPResponseReadWriteAcker) AckConnection(tracePropagation string) erro
type ResponseWriter interface {
WriteRespHeaders(status int, header http.Header) error
AddTrailer(trailerName, trailerValue string)
io.Writer
}
@@ -197,10 +206,18 @@ type ConnectedFuse interface {
IsConnected() bool
}
func IsServerSentEvent(headers http.Header) bool {
if contentType := headers.Get("content-type"); contentType != "" {
return strings.HasPrefix(strings.ToLower(contentType), "text/event-stream")
// Helper method to let the caller know what content-types should require a flush on every
// write to a ResponseWriter.
func shouldFlush(headers http.Header) bool {
if contentType := headers.Get(contentTypeHeader); contentType != "" {
contentType = strings.ToLower(contentType)
for _, c := range flushableContentTypes {
if strings.HasPrefix(contentType, c) {
return true
}
}
}
return false
}
-39
View File
@@ -6,11 +6,9 @@ import (
"io"
"math/rand"
"net/http"
"testing"
"time"
"github.com/rs/zerolog"
"github.com/stretchr/testify/assert"
"github.com/cloudflare/cloudflared/tracing"
tunnelpogs "github.com/cloudflare/cloudflared/tunnelrpc/pogs"
@@ -197,40 +195,3 @@ func (mcf mockConnectedFuse) Connected() {}
func (mcf mockConnectedFuse) IsConnected() bool {
return true
}
func TestIsEventStream(t *testing.T) {
tests := []struct {
headers http.Header
isEventStream bool
}{
{
headers: newHeader("Content-Type", "text/event-stream"),
isEventStream: true,
},
{
headers: newHeader("content-type", "text/event-stream"),
isEventStream: true,
},
{
headers: newHeader("Content-Type", "text/event-stream; charset=utf-8"),
isEventStream: true,
},
{
headers: newHeader("Content-Type", "application/json"),
isEventStream: false,
},
{
headers: http.Header{},
isEventStream: false,
},
}
for _, test := range tests {
assert.Equal(t, test.isEventStream, IsServerSentEvent(test.headers))
}
}
func newHeader(key, value string) http.Header {
header := http.Header{}
header.Add(key, value)
return header
}
+4
View File
@@ -259,6 +259,10 @@ type h2muxRespWriter struct {
*h2mux.MuxedStream
}
func (rp *h2muxRespWriter) AddTrailer(trailerName, trailerValue string) {
// do nothing. we don't support trailers over h2mux
}
func (rp *h2muxRespWriter) WriteRespHeaders(status int, header http.Header) error {
headers := H1ResponseToH2ResponseHeaders(status, header)
headers = append(headers, h2mux.Header{Name: ResponseMetaHeader, Value: responseMetaHeaderOrigin})
+25 -11
View File
@@ -149,9 +149,10 @@ func (c *HTTP2Connection) ServeHTTP(w http.ResponseWriter, r *http.Request) {
rws := NewHTTPResponseReadWriterAcker(respWriter, r)
if err := originProxy.ProxyTCP(r.Context(), rws, &TCPRequest{
Dest: host,
CFRay: FindCfRayHeader(r),
LBProbe: IsLBProbeRequest(r),
Dest: host,
CFRay: FindCfRayHeader(r),
LBProbe: IsLBProbeRequest(r),
CfTraceID: r.Header.Get(tracing.TracerContextName),
}); err != nil {
respWriter.WriteErrorResponse()
}
@@ -190,11 +191,12 @@ func (c *HTTP2Connection) close() {
}
type http2RespWriter struct {
r io.Reader
w http.ResponseWriter
flusher http.Flusher
shouldFlush bool
log *zerolog.Logger
r io.Reader
w http.ResponseWriter
flusher http.Flusher
shouldFlush bool
statusWritten bool
log *zerolog.Logger
}
func NewHTTP2RespWriter(r *http.Request, w http.ResponseWriter, connType Type, log *zerolog.Logger) (*http2RespWriter, error) {
@@ -218,11 +220,20 @@ func NewHTTP2RespWriter(r *http.Request, w http.ResponseWriter, connType Type, l
}, nil
}
func (rp *http2RespWriter) AddTrailer(trailerName, trailerValue string) {
if !rp.statusWritten {
rp.log.Warn().Msg("Tried to add Trailer to response before status written. Ignoring...")
return
}
rp.w.Header().Add(http2.TrailerPrefix+trailerName, trailerValue)
}
func (rp *http2RespWriter) WriteRespHeaders(status int, header http.Header) error {
dest := rp.w.Header()
userHeaders := make(http.Header, len(header))
for name, values := range header {
// Since these are http2 headers, they're required to be lowercase
// lowercase headers for simplicity check
h2name := strings.ToLower(name)
if h2name == "content-length" {
@@ -233,7 +244,7 @@ func (rp *http2RespWriter) WriteRespHeaders(status int, header http.Header) erro
if h2name == tracing.IntCloudflaredTracingHeader {
// Add cf-int-cloudflared-tracing header outside of serialized userHeaders
rp.w.Header()[tracing.CanonicalCloudflaredTracingHeader] = values
dest[tracing.CanonicalCloudflaredTracingHeader] = values
continue
}
@@ -246,18 +257,21 @@ func (rp *http2RespWriter) WriteRespHeaders(status int, header http.Header) erro
// Perform user header serialization and set them in the single header
dest.Set(CanonicalResponseUserHeaders, SerializeHeaders(userHeaders))
rp.setResponseMetaHeader(responseMetaHeaderOrigin)
// HTTP2 removes support for 101 Switching Protocols https://tools.ietf.org/html/rfc7540#section-8.1.1
if status == http.StatusSwitchingProtocols {
status = http.StatusOK
}
rp.w.WriteHeader(status)
if IsServerSentEvent(header) {
if shouldFlush(header) {
rp.shouldFlush = true
}
if rp.shouldFlush {
rp.flusher.Flush()
}
rp.statusWritten = true
return nil
}
+2 -2
View File
@@ -209,8 +209,8 @@ func NewProtocolSelector(
threshold := switchThreshold(namedTunnel.Credentials.AccountTag)
fetchedProtocol, err := getProtocol([]Protocol{QUIC, HTTP2}, fetchFunc, threshold)
if err != nil {
log.Err(err).Msg("Unable to lookup protocol. Defaulting to `http2`. If this fails, you can set `--protocol h2mux` in your cloudflared command.")
if err != nil && protocolFlag == "auto" {
log.Err(err).Msg("Unable to lookup protocol. Defaulting to `http2`. If this fails, you can attempt `--protocol quic` instead.")
return &staticProtocolSelector{
current: HTTP2,
}, nil
+92 -8
View File
@@ -9,6 +9,7 @@ import (
"net/http"
"strconv"
"strings"
"sync/atomic"
"time"
"github.com/google/uuid"
@@ -19,6 +20,7 @@ import (
"github.com/cloudflare/cloudflared/datagramsession"
"github.com/cloudflare/cloudflared/ingress"
"github.com/cloudflare/cloudflared/packet"
quicpogs "github.com/cloudflare/cloudflared/quic"
"github.com/cloudflare/cloudflared/tracing"
tunnelpogs "github.com/cloudflare/cloudflared/tunnelrpc/pogs"
@@ -45,7 +47,8 @@ type QUICConnection struct {
// sessionManager tracks active sessions. It receives datagrams from quic connection via datagramMuxer
sessionManager datagramsession.Manager
// datagramMuxer mux/demux datagrams from quic connection
datagramMuxer *quicpogs.DatagramMuxer
datagramMuxer quicpogs.BaseDatagramMuxer
packetRouter *packetRouter
controlStreamHandler ControlStreamHandler
connOptions *tunnelpogs.ConnectionOptions
}
@@ -59,15 +62,29 @@ func NewQUICConnection(
connOptions *tunnelpogs.ConnectionOptions,
controlStreamHandler ControlStreamHandler,
logger *zerolog.Logger,
icmpProxy ingress.ICMPProxy,
) (*QUICConnection, error) {
session, err := quic.DialAddr(edgeAddr.String(), tlsConfig, quicConfig)
if err != nil {
return nil, &EdgeQuicDialError{Cause: err}
}
demuxChan := make(chan *quicpogs.SessionDatagram, demuxChanCapacity)
datagramMuxer := quicpogs.NewDatagramMuxer(session, logger, demuxChan)
sessionManager := datagramsession.NewManager(logger, datagramMuxer.MuxSession, demuxChan)
sessionDemuxChan := make(chan *packet.Session, demuxChanCapacity)
var (
datagramMuxer quicpogs.BaseDatagramMuxer
pr *packetRouter
)
if icmpProxy != nil {
pr = &packetRouter{
muxer: quicpogs.NewDatagramMuxerV2(session, logger, sessionDemuxChan),
icmpProxy: icmpProxy,
logger: logger,
}
datagramMuxer = pr.muxer
} else {
datagramMuxer = quicpogs.NewDatagramMuxer(session, logger, sessionDemuxChan)
}
sessionManager := datagramsession.NewManager(logger, datagramMuxer.SendToSession, sessionDemuxChan)
return &QUICConnection{
session: session,
@@ -75,6 +92,7 @@ func NewQUICConnection(
logger: logger,
sessionManager: sessionManager,
datagramMuxer: datagramMuxer,
packetRouter: pr,
controlStreamHandler: controlStreamHandler,
connOptions: connOptions,
}, nil
@@ -115,6 +133,12 @@ func (q *QUICConnection) Serve(ctx context.Context) error {
defer cancel()
return q.datagramMuxer.ServeReceive(ctx)
})
if q.packetRouter != nil {
errGroup.Go(func() error {
defer cancel()
return q.packetRouter.serve(ctx)
})
}
return errGroup.Wait()
}
@@ -156,9 +180,10 @@ func (q *QUICConnection) runStream(quicStream quic.Stream) {
defer stream.Close()
// we are going to fuse readers/writers from stream <- cloudflared -> origin, and we want to guarantee that
// code executed in the code path of handleStream don't trigger an earlier close to the downstream stream.
// So, we wrap the stream with a no-op closer and only this method can actually close the stream.
noCloseStream := &nopCloserReadWriter{stream}
// code executed in the code path of handleStream don't trigger an earlier close to the downstream write stream.
// So, we wrap the stream with a no-op write closer and only this method can actually close write side of the stream.
// A call to close will simulate a close to the read-side, which will fail subsequent reads.
noCloseStream := &nopCloserReadWriter{ReadWriteCloser: stream}
if err := q.handleStream(ctx, noCloseStream); err != nil {
q.logger.Err(err).Msg("Failed to handle QUIC stream")
}
@@ -302,6 +327,32 @@ func (q *QUICConnection) UpdateConfiguration(ctx context.Context, version int32,
return q.orchestrator.UpdateConfig(version, config)
}
type packetRouter struct {
muxer *quicpogs.DatagramMuxerV2
icmpProxy ingress.ICMPProxy
logger *zerolog.Logger
}
func (pr *packetRouter) serve(ctx context.Context) error {
icmpDecoder := packet.NewICMPDecoder()
for {
pk, err := pr.muxer.ReceivePacket(ctx)
if err != nil {
return err
}
icmpPacket, err := icmpDecoder.Decode(pk)
if err != nil {
pr.logger.Err(err).Msg("Failed to decode ICMP packet from quic datagram")
continue
}
if err := pr.icmpProxy.Request(icmpPacket, pr.muxer); err != nil {
pr.logger.Err(err).Str("src", icmpPacket.Src.String()).Str("dst", icmpPacket.Dst.String()).Msg("Failed to send ICMP packet")
continue
}
}
}
// streamReadWriteAcker is a light wrapper over QUIC streams with a callback to send response back to
// the client.
type streamReadWriteAcker struct {
@@ -326,6 +377,10 @@ func newHTTPResponseAdapter(s *quicpogs.RequestServerStream) httpResponseAdapter
return httpResponseAdapter{s}
}
func (hrw httpResponseAdapter) AddTrailer(trailerName, trailerValue string) {
// we do not support trailers over QUIC
}
func (hrw httpResponseAdapter) WriteRespHeaders(status int, header http.Header) error {
metadata := make([]quicpogs.Metadata, 0)
metadata = append(metadata, quicpogs.Metadata{Key: "HttpStatus", Val: strconv.Itoa(status)})
@@ -408,10 +463,39 @@ func isTransferEncodingChunked(req *http.Request) bool {
return strings.Contains(strings.ToLower(transferEncodingVal), "chunked")
}
// A helper struct that guarantees a call to close only affects read side, but not write side.
type nopCloserReadWriter struct {
io.ReadWriteCloser
// for use by Read only
// we don't need a memory barrier here because there is an implicit assumption that
// Read calls can't happen concurrently by different go-routines.
sawEOF bool
// should be updated and read using atomic primitives.
// value is read in Read method and written in Close method, which could be done by different
// go-routines.
closed uint32
}
func (n *nopCloserReadWriter) Close() error {
func (np *nopCloserReadWriter) Read(p []byte) (n int, err error) {
if np.sawEOF {
return 0, io.EOF
}
if atomic.LoadUint32(&np.closed) > 0 {
return 0, fmt.Errorf("closed by handler")
}
n, err = np.ReadWriteCloser.Read(p)
if err == io.EOF {
np.sawEOF = true
}
return
}
func (np *nopCloserReadWriter) Close() error {
atomic.StoreUint32(&np.closed, 1)
return nil
}
+52
View File
@@ -10,6 +10,7 @@ import (
"net/http"
"net/url"
"os"
"strings"
"sync"
"testing"
"time"
@@ -527,6 +528,44 @@ func TestServeUDPSession(t *testing.T) {
cancel()
}
func TestNopCloserReadWriterCloseBeforeEOF(t *testing.T) {
readerWriter := nopCloserReadWriter{ReadWriteCloser: &mockReaderNoopWriter{Reader: strings.NewReader("123456789")}}
buffer := make([]byte, 5)
n, err := readerWriter.Read(buffer)
require.NoError(t, err)
require.Equal(t, n, 5)
// close
require.NoError(t, readerWriter.Close())
// read should get error
n, err = readerWriter.Read(buffer)
require.Equal(t, n, 0)
require.Equal(t, err, fmt.Errorf("closed by handler"))
}
func TestNopCloserReadWriterCloseAfterEOF(t *testing.T) {
readerWriter := nopCloserReadWriter{ReadWriteCloser: &mockReaderNoopWriter{Reader: strings.NewReader("123456789")}}
buffer := make([]byte, 20)
n, err := readerWriter.Read(buffer)
require.NoError(t, err)
require.Equal(t, n, 9)
// force another read to read eof
n, err = readerWriter.Read(buffer)
require.Equal(t, err, io.EOF)
// close
require.NoError(t, readerWriter.Close())
// read should get EOF still
n, err = readerWriter.Read(buffer)
require.Equal(t, n, 0)
require.Equal(t, err, io.EOF)
}
func serveSession(ctx context.Context, qc *QUICConnection, edgeQUICSession quic.Connection, closeType closeReason, expectedReason string, t *testing.T) {
var (
payload = []byte(t.Name())
@@ -643,7 +682,20 @@ func testQUICConnection(udpListenerAddr net.Addr, t *testing.T) *QUICConnection
&tunnelpogs.ConnectionOptions{},
fakeControlStream{},
&log,
nil,
)
require.NoError(t, err)
return qc
}
type mockReaderNoopWriter struct {
io.Reader
}
func (m *mockReaderNoopWriter) Write(p []byte) (n int, err error) {
return len(p), nil
}
func (m *mockReaderNoopWriter) Close() error {
return nil
}
+4 -4
View File
@@ -9,7 +9,7 @@ import (
"github.com/google/uuid"
"github.com/rs/zerolog"
quicpogs "github.com/cloudflare/cloudflared/quic"
"github.com/cloudflare/cloudflared/packet"
)
const (
@@ -37,7 +37,7 @@ type manager struct {
registrationChan chan *registerSessionEvent
unregistrationChan chan *unregisterSessionEvent
sendFunc transportSender
receiveChan <-chan *quicpogs.SessionDatagram
receiveChan <-chan *packet.Session
closedChan <-chan struct{}
sessions map[uuid.UUID]*Session
log *zerolog.Logger
@@ -45,7 +45,7 @@ type manager struct {
timeout time.Duration
}
func NewManager(log *zerolog.Logger, sendF transportSender, receiveChan <-chan *quicpogs.SessionDatagram) *manager {
func NewManager(log *zerolog.Logger, sendF transportSender, receiveChan <-chan *packet.Session) *manager {
return &manager{
registrationChan: make(chan *registerSessionEvent),
unregistrationChan: make(chan *unregisterSessionEvent),
@@ -163,7 +163,7 @@ func (m *manager) unregisterSession(unregistration *unregisterSessionEvent) {
}
}
func (m *manager) sendToSession(datagram *quicpogs.SessionDatagram) {
func (m *manager) sendToSession(datagram *packet.Session) {
session, ok := m.sessions[datagram.ID]
if !ok {
m.log.Error().Str("sessionID", datagram.ID.String()).Msg("session not found")
+7 -7
View File
@@ -15,7 +15,7 @@ import (
"github.com/stretchr/testify/require"
"golang.org/x/sync/errgroup"
quicpogs "github.com/cloudflare/cloudflared/quic"
"github.com/cloudflare/cloudflared/packet"
)
var (
@@ -29,7 +29,7 @@ func TestManagerServe(t *testing.T) {
remoteUnregisterMsg = "eyeball closed connection"
)
requestChan := make(chan *quicpogs.SessionDatagram)
requestChan := make(chan *packet.Session)
transport := mockQUICTransport{
sessions: make(map[uuid.UUID]chan []byte),
}
@@ -241,9 +241,9 @@ type mockQUICTransport struct {
sessions map[uuid.UUID]chan []byte
}
func (me *mockQUICTransport) MuxSession(id uuid.UUID, payload []byte) error {
session := me.sessions[id]
session <- payload
func (me *mockQUICTransport) MuxSession(session *packet.Session) error {
s := me.sessions[session.ID]
s <- session.Payload
return nil
}
@@ -255,9 +255,9 @@ type mockEyeballSession struct {
respReceiver <-chan []byte
}
func (me *mockEyeballSession) serve(ctx context.Context, requestChan chan *quicpogs.SessionDatagram) error {
func (me *mockEyeballSession) serve(ctx context.Context, requestChan chan *packet.Session) error {
for i := 0; i < me.expectedMsgCount; i++ {
requestChan <- &quicpogs.SessionDatagram{
requestChan <- &packet.Session{
ID: me.id,
Payload: me.expectedMsg,
}
+12 -5
View File
@@ -2,6 +2,7 @@ package datagramsession
import (
"context"
"errors"
"fmt"
"io"
"net"
@@ -9,6 +10,8 @@ import (
"github.com/google/uuid"
"github.com/rs/zerolog"
"github.com/cloudflare/cloudflared/packet"
)
const (
@@ -19,7 +22,7 @@ func SessionIdleErr(timeout time.Duration) error {
return fmt.Errorf("session idle for %v", timeout)
}
type transportSender func(sessionID uuid.UUID, payload []byte) error
type transportSender func(session *packet.Session) error
// Session is a bidirectional pipe of datagrams between transport and dstConn
// Destination can be a connection with origin or with eyeball
@@ -47,10 +50,10 @@ func (s *Session) Serve(ctx context.Context, closeAfterIdle time.Duration) (clos
readBuffer := make([]byte, maxPacketSize)
for {
if closeSession, err := s.dstToTransport(readBuffer); err != nil {
if err != net.ErrClosed {
s.log.Error().Err(err).Msg("Failed to send session payload from destination to transport")
if errors.Is(err, net.ErrClosed) {
s.log.Debug().Msg("Destination connection closed")
} else {
s.log.Debug().Msg("Session cannot read from destination because the connection is closed")
s.log.Error().Err(err).Msg("Failed to send session payload from destination to transport")
}
if closeSession {
s.closeChan <- err
@@ -101,7 +104,11 @@ func (s *Session) dstToTransport(buffer []byte) (closeSession bool, err error) {
s.markActive()
// https://pkg.go.dev/io#Reader suggests caller should always process n > 0 bytes
if n > 0 || err == nil {
if sendErr := s.sendFunc(s.ID, buffer[:n]); sendErr != nil {
session := packet.Session{
ID: s.ID,
Payload: buffer[:n],
}
if sendErr := s.sendFunc(&session); sendErr != nil {
return false, sendErr
}
}
+9 -9
View File
@@ -15,7 +15,7 @@ import (
"github.com/stretchr/testify/require"
"golang.org/x/sync/errgroup"
quicpogs "github.com/cloudflare/cloudflared/quic"
"github.com/cloudflare/cloudflared/packet"
)
// TestCloseSession makes sure a session will stop after context is done
@@ -118,7 +118,7 @@ func testActiveSessionNotClosed(t *testing.T, readFromDst bool, writeToDst bool)
cfdConn, originConn := net.Pipe()
payload := testPayload(sessionID)
respChan := make(chan *quicpogs.SessionDatagram)
respChan := make(chan *packet.Session)
sender := newMockTransportSender(sessionID, payload)
mg := NewManager(&nopLogger, sender.muxSession, respChan)
session := mg.newSession(sessionID, cfdConn)
@@ -243,12 +243,12 @@ func newMockTransportSender(expectedSessionID uuid.UUID, expectedPayload []byte)
}
}
func (mts *mockTransportSender) muxSession(sessionID uuid.UUID, payload []byte) error {
if sessionID != mts.expectedSessionID {
return fmt.Errorf("Expect session %s, got %s", mts.expectedSessionID, sessionID)
func (mts *mockTransportSender) muxSession(session *packet.Session) error {
if session.ID != mts.expectedSessionID {
return fmt.Errorf("Expect session %s, got %s", mts.expectedSessionID, session.ID)
}
if !bytes.Equal(payload, mts.expectedPayload) {
return fmt.Errorf("Expect %v, read %v", mts.expectedPayload, payload)
if !bytes.Equal(session.Payload, mts.expectedPayload) {
return fmt.Errorf("Expect %v, read %v", mts.expectedPayload, session.Payload)
}
return nil
}
@@ -258,7 +258,7 @@ type sendOnceTransportSender struct {
sentChan chan struct{}
}
func (sots *sendOnceTransportSender) muxSession(sessionID uuid.UUID, payload []byte) error {
func (sots *sendOnceTransportSender) muxSession(session *packet.Session) error {
defer close(sots.sentChan)
return sots.baseSender.muxSession(sessionID, payload)
return sots.baseSender.muxSession(session)
}
+3 -2
View File
@@ -34,9 +34,9 @@ require (
go.opentelemetry.io/proto/otlp v0.15.0
go.uber.org/automaxprocs v1.4.0
golang.org/x/crypto v0.0.0-20220427172511-eb4f295cb31f
golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e
golang.org/x/net v0.0.0-20220812174116-3211cb980234
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211
google.golang.org/protobuf v1.28.0
gopkg.in/coreos/go-oidc.v2 v2.2.1
@@ -67,6 +67,7 @@ require (
github.com/gobwas/httphead v0.0.0-20200921212729-da3d93bc3c58 // indirect
github.com/gobwas/pool v0.2.1 // indirect
github.com/golang/protobuf v1.5.2 // indirect
github.com/google/gopacket v1.1.19 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0 // indirect
github.com/grpc-ecosystem/grpc-opentracing v0.0.0-20180507213350-8e809c8a8645 // indirect
github.com/kylelemons/godebug v1.1.0 // indirect
+6
View File
@@ -292,6 +292,8 @@ github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/gopacket v1.1.19 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF8=
github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
@@ -710,6 +712,8 @@ golang.org/x/net v0.0.0-20211209124913-491a49abca63/go.mod h1:9nx3DQGgdP8bBQD5qx
golang.org/x/net v0.0.0-20211216030914-fe4d6282115f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e h1:TsQ7F31D3bUCLeqPT0u+yjp1guoArKaNKmCr22PYgTQ=
golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.0.0-20220812174116-3211cb980234 h1:RDqmgfe7SvlMWoqC3xwQ2blLO3fcWcxMa3eBLRdRW7E=
golang.org/x/net v0.0.0-20220812174116-3211cb980234/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
@@ -816,6 +820,8 @@ golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a h1:dGzPydgVsqGcTRVwiLJ1jVbufYwmzD3LfVPLKsKg+0k=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10 h1:WIoqL4EROvwiPdUtaip4VcDdpZ4kha7wBWZrbVKCIZg=
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY=
+229
View File
@@ -0,0 +1,229 @@
//go:build darwin
package ingress
import (
"context"
"fmt"
"math"
"net"
"net/netip"
"strconv"
"sync"
"github.com/google/gopacket/layers"
"github.com/pkg/errors"
"github.com/rs/zerolog"
"golang.org/x/net/icmp"
"github.com/cloudflare/cloudflared/packet"
)
// TODO: TUN-6654 Extend support to IPv6
// On Darwin, a non-privileged ICMP socket can read messages from all echo IDs, so we use it for all sources.
type icmpProxy struct {
// TODO: TUN-6588 clean up flows
srcFlowTracker *packet.FlowTracker
echoIDTracker *echoIDTracker
conn *icmp.PacketConn
logger *zerolog.Logger
}
// echoIDTracker tracks which ID has been assigned. It first loops through assignment from lastAssignment to then end,
// then from the beginning to lastAssignment.
// ICMP echo are short lived. By the time an ID is revisited, it should have been released.
type echoIDTracker struct {
lock sync.RWMutex
// maps the source IP to an echo ID obtained from assignment
srcIPMapping map[netip.Addr]uint16
// assignment tracks if an ID is assigned using index as the ID
// The size of the array is math.MaxUint16 because echo ID is 2 bytes
assignment [math.MaxUint16]bool
// nextAssignment is the next number to check for assigment
nextAssignment uint16
}
func newEchoIDTracker() *echoIDTracker {
return &echoIDTracker{
srcIPMapping: make(map[netip.Addr]uint16),
}
}
func (eit *echoIDTracker) get(srcIP netip.Addr) (uint16, bool) {
eit.lock.RLock()
defer eit.lock.RUnlock()
id, ok := eit.srcIPMapping[srcIP]
return id, ok
}
func (eit *echoIDTracker) assign(srcIP netip.Addr) (uint16, bool) {
eit.lock.Lock()
defer eit.lock.Unlock()
if eit.nextAssignment == math.MaxUint16 {
eit.nextAssignment = 0
}
for i, assigned := range eit.assignment[eit.nextAssignment:] {
if !assigned {
echoID := uint16(i) + eit.nextAssignment
eit.set(srcIP, echoID)
return echoID, true
}
}
for i, assigned := range eit.assignment[0:eit.nextAssignment] {
if !assigned {
echoID := uint16(i)
eit.set(srcIP, echoID)
return echoID, true
}
}
return 0, false
}
// Caller should hold the lock
func (eit *echoIDTracker) set(srcIP netip.Addr, echoID uint16) {
eit.assignment[echoID] = true
eit.srcIPMapping[srcIP] = echoID
eit.nextAssignment = echoID + 1
}
func (eit *echoIDTracker) release(srcIP netip.Addr, id uint16) bool {
eit.lock.Lock()
defer eit.lock.Unlock()
currentID, ok := eit.srcIPMapping[srcIP]
if ok && id == currentID {
delete(eit.srcIPMapping, srcIP)
eit.assignment[id] = false
return true
}
return false
}
type echoFlowID uint16
func (snf echoFlowID) Type() string {
return "echoID"
}
func (snf echoFlowID) String() string {
return strconv.FormatUint(uint64(snf), 10)
}
func newICMPProxy(listenIP netip.Addr, logger *zerolog.Logger) (ICMPProxy, error) {
conn, err := newICMPConn(listenIP)
if err != nil {
return nil, err
}
return &icmpProxy{
srcFlowTracker: packet.NewFlowTracker(),
echoIDTracker: newEchoIDTracker(),
conn: conn,
logger: logger,
}, nil
}
func (ip *icmpProxy) Request(pk *packet.ICMP, responder packet.FlowResponder) error {
if pk == nil {
return errPacketNil
}
switch body := pk.Message.Body.(type) {
case *icmp.Echo:
return ip.sendICMPEchoRequest(pk, body, responder)
default:
return fmt.Errorf("sending ICMP %s is not implemented", pk.Type)
}
}
// Serve listens for responses to the requests until context is done
func (ip *icmpProxy) Serve(ctx context.Context) error {
go func() {
<-ctx.Done()
ip.conn.Close()
}()
buf := make([]byte, mtu)
encoder := packet.NewEncoder()
for {
n, src, err := ip.conn.ReadFrom(buf)
if err != nil {
return err
}
// TODO: TUN-6654 Check for IPv6
msg, err := icmp.ParseMessage(int(layers.IPProtocolICMPv4), buf[:n])
if err != nil {
ip.logger.Error().Err(err).Str("src", src.String()).Msg("Failed to parse ICMP message")
continue
}
switch body := msg.Body.(type) {
case *icmp.Echo:
if err := ip.handleEchoResponse(encoder, msg, body); err != nil {
ip.logger.Error().Err(err).
Str("src", src.String()).
Str("flowID", echoFlowID(body.ID).String()).
Msg("Failed to handle ICMP response")
continue
}
default:
ip.logger.Warn().
Str("icmpType", fmt.Sprintf("%s", msg.Type)).
Msgf("Responding to this type of ICMP is not implemented")
continue
}
}
}
func (ip *icmpProxy) sendICMPEchoRequest(pk *packet.ICMP, echo *icmp.Echo, responder packet.FlowResponder) error {
echoID, ok := ip.echoIDTracker.get(pk.Src)
if !ok {
echoID, ok = ip.echoIDTracker.assign(pk.Src)
if !ok {
return fmt.Errorf("failed to assign unique echo ID")
}
flowID := echoFlowID(echoID)
flow := packet.Flow{
Src: pk.Src,
Dst: pk.Dst,
Responder: responder,
}
if replaced := ip.srcFlowTracker.Register(flowID, &flow, true); replaced {
ip.logger.Info().Str("src", flow.Src.String()).Str("dst", flow.Dst.String()).Msg("Replaced flow")
}
}
echo.ID = int(echoID)
var pseudoHeader []byte = nil
serializedMsg, err := pk.Marshal(pseudoHeader)
if err != nil {
return errors.Wrap(err, "Failed to encode ICMP message")
}
// The address needs to be of type UDPAddr when conn is created without priviledge
_, err = ip.conn.WriteTo(serializedMsg, &net.UDPAddr{
IP: pk.Dst.AsSlice(),
})
return err
}
func (ip *icmpProxy) handleEchoResponse(encoder *packet.Encoder, msg *icmp.Message, echo *icmp.Echo) error {
flowID := echoFlowID(echo.ID)
flow, ok := ip.srcFlowTracker.Get(flowID)
if !ok {
return fmt.Errorf("flow not found")
}
icmpPacket := packet.ICMP{
IP: &packet.IP{
Src: flow.Dst,
Dst: flow.Src,
Protocol: layers.IPProtocol(msg.Type.Protocol()),
},
Message: msg,
}
serializedPacket, err := encoder.Encode(&icmpPacket)
if err != nil {
return errors.Wrap(err, "Failed to encode ICMP message")
}
if err := flow.Responder.SendPacket(serializedPacket); err != nil {
return errors.Wrap(err, "Failed to send packet to the edge")
}
return nil
}
+91
View File
@@ -0,0 +1,91 @@
//go:build darwin
package ingress
import (
"math"
"net/netip"
"testing"
"github.com/stretchr/testify/require"
)
func TestSingleEchoIDTracker(t *testing.T) {
tracker := newEchoIDTracker()
srcIP := netip.MustParseAddr("127.0.0.1")
echoID, ok := tracker.get(srcIP)
require.False(t, ok)
require.Equal(t, uint16(0), echoID)
// not assigned yet, so nothing to release
require.False(t, tracker.release(srcIP, echoID))
echoID, ok = tracker.assign(srcIP)
require.True(t, ok)
require.Equal(t, uint16(0), echoID)
echoID, ok = tracker.get(srcIP)
require.True(t, ok)
require.Equal(t, uint16(0), echoID)
// releasing a different ID returns false
require.False(t, tracker.release(srcIP, 1999))
require.True(t, tracker.release(srcIP, echoID))
// releasing the second time returns false
require.False(t, tracker.release(srcIP, echoID))
echoID, ok = tracker.get(srcIP)
require.False(t, ok)
require.Equal(t, uint16(0), echoID)
// Move to the next IP
echoID, ok = tracker.assign(srcIP)
require.True(t, ok)
require.Equal(t, uint16(1), echoID)
}
func TestFullEchoIDTracker(t *testing.T) {
tracker := newEchoIDTracker()
firstIP := netip.MustParseAddr("172.16.0.1")
srcIP := firstIP
for i := uint16(0); i < math.MaxUint16; i++ {
echoID, ok := tracker.assign(srcIP)
require.True(t, ok)
require.Equal(t, i, echoID)
echoID, ok = tracker.get(srcIP)
require.True(t, ok)
require.Equal(t, i, echoID)
srcIP = srcIP.Next()
}
// All echo IDs are assigned
echoID, ok := tracker.assign(srcIP.Next())
require.False(t, ok)
require.Equal(t, uint16(0), echoID)
srcIP = firstIP
for i := uint16(0); i < math.MaxUint16; i++ {
ok := tracker.release(srcIP, i)
require.True(t, ok)
echoID, ok = tracker.get(srcIP)
require.False(t, ok)
require.Equal(t, uint16(0), echoID)
srcIP = srcIP.Next()
}
// The IDs are assignable again
srcIP = firstIP
for i := uint16(0); i < math.MaxUint16; i++ {
echoID, ok := tracker.assign(srcIP)
require.True(t, ok)
require.Equal(t, i, echoID)
echoID, ok = tracker.get(srcIP)
require.True(t, ok)
require.Equal(t, i, echoID)
srcIP = srcIP.Next()
}
}
+15
View File
@@ -0,0 +1,15 @@
//go:build !darwin && !linux
package ingress
import (
"fmt"
"net/netip"
"runtime"
"github.com/rs/zerolog"
)
func newICMPProxy(listenIP netip.Addr, logger *zerolog.Logger) (ICMPProxy, error) {
return nil, fmt.Errorf("ICMP proxy is not implemented on %s", runtime.GOOS)
}
+267
View File
@@ -0,0 +1,267 @@
//go:build linux
package ingress
import (
"context"
"fmt"
"net"
"net/netip"
"sync"
"sync/atomic"
"time"
"github.com/google/gopacket/layers"
"github.com/pkg/errors"
"github.com/rs/zerolog"
"golang.org/x/net/icmp"
"github.com/cloudflare/cloudflared/packet"
)
// The request echo ID is rewritten to the port of the socket. The kernel uses the reply echo ID to demultiplex
// We can open a socket for each source so multiple sources requesting the same destination doesn't collide
type icmpProxy struct {
srcToFlowTracker *srcToFlowTracker
listenIP netip.Addr
logger *zerolog.Logger
shutdownC chan struct{}
}
func newICMPProxy(listenIP netip.Addr, logger *zerolog.Logger) (ICMPProxy, error) {
if err := testPermission(listenIP); err != nil {
return nil, err
}
return &icmpProxy{
srcToFlowTracker: newSrcToConnTracker(),
listenIP: listenIP,
logger: logger,
shutdownC: make(chan struct{}),
}, nil
}
func testPermission(listenIP netip.Addr) error {
// Opens a non-privileged ICMP socket. On Linux the group ID of the process needs to be in ping_group_range
// For more information, see https://man7.org/linux/man-pages/man7/icmp.7.html and https://lwn.net/Articles/422330/
conn, err := newICMPConn(listenIP)
if err != nil {
// TODO: TUN-6715 check if cloudflared is in ping_group_range if the check failed. If not log instruction to
// change the group ID
return err
}
// This conn is only to test if cloudflared has permission to open this type of socket
conn.Close()
return nil
}
func (ip *icmpProxy) Request(pk *packet.ICMP, responder packet.FlowResponder) error {
if pk == nil {
return errPacketNil
}
switch body := pk.Message.Body.(type) {
case *icmp.Echo:
return ip.sendICMPEchoRequest(pk, body, responder)
default:
return fmt.Errorf("sending ICMP %s is not implemented", pk.Type)
}
}
func (ip *icmpProxy) Serve(ctx context.Context) error {
<-ctx.Done()
close(ip.shutdownC)
return ctx.Err()
}
func (ip *icmpProxy) sendICMPEchoRequest(pk *packet.ICMP, echo *icmp.Echo, responder packet.FlowResponder) error {
icmpFlow, ok := ip.srcToFlowTracker.get(pk.Src)
if ok {
return icmpFlow.send(pk)
}
conn, err := newICMPConn(ip.listenIP)
if err != nil {
return err
}
flow := packet.Flow{
Src: pk.Src,
Dst: pk.Dst,
Responder: responder,
}
icmpFlow = newICMPFlow(conn, &flow, uint16(echo.ID), ip.logger)
go func() {
defer ip.srcToFlowTracker.delete(pk.Src)
if err := icmpFlow.serve(ip.shutdownC, defaultCloseAfterIdle); err != nil {
ip.logger.Debug().Err(err).Uint16("flowID", icmpFlow.echoID).Msg("flow terminated")
}
}()
ip.srcToFlowTracker.set(pk.Src, icmpFlow)
return icmpFlow.send(pk)
}
type srcIPFlowID netip.Addr
func (sifd srcIPFlowID) Type() string {
return "srcIP"
}
func (sifd srcIPFlowID) String() string {
return netip.Addr(sifd).String()
}
type srcToFlowTracker struct {
lock sync.RWMutex
// srcIPToConn tracks source IP to ICMP connection
srcToFlow map[netip.Addr]*icmpFlow
}
func newSrcToConnTracker() *srcToFlowTracker {
return &srcToFlowTracker{
srcToFlow: make(map[netip.Addr]*icmpFlow),
}
}
func (sft *srcToFlowTracker) get(srcIP netip.Addr) (*icmpFlow, bool) {
sft.lock.RLock()
defer sft.lock.RUnlock()
flow, ok := sft.srcToFlow[srcIP]
return flow, ok
}
func (sft *srcToFlowTracker) set(srcIP netip.Addr, flow *icmpFlow) {
sft.lock.Lock()
defer sft.lock.Unlock()
sft.srcToFlow[srcIP] = flow
}
func (sft *srcToFlowTracker) delete(srcIP netip.Addr) {
sft.lock.Lock()
defer sft.lock.Unlock()
delete(sft.srcToFlow, srcIP)
}
type icmpFlow struct {
conn *icmp.PacketConn
flow *packet.Flow
echoID uint16
// last active unix time. Unit is seconds
lastActive int64
logger *zerolog.Logger
}
func newICMPFlow(conn *icmp.PacketConn, flow *packet.Flow, echoID uint16, logger *zerolog.Logger) *icmpFlow {
return &icmpFlow{
conn: conn,
flow: flow,
echoID: echoID,
lastActive: time.Now().Unix(),
logger: logger,
}
}
func (f *icmpFlow) serve(shutdownC chan struct{}, closeAfterIdle time.Duration) error {
errC := make(chan error)
go func() {
errC <- f.listenResponse()
}()
checkIdleTicker := time.NewTicker(closeAfterIdle)
defer f.conn.Close()
defer checkIdleTicker.Stop()
for {
select {
case err := <-errC:
return err
case <-shutdownC:
return nil
case <-checkIdleTicker.C:
now := time.Now().Unix()
lastActive := atomic.LoadInt64(&f.lastActive)
if now > lastActive+int64(closeAfterIdle.Seconds()) {
return errFlowInactive
}
}
}
}
func (f *icmpFlow) send(pk *packet.ICMP) error {
f.updateLastActive()
// For IPv4, the pseudoHeader is not used because the checksum is always calculated
var pseudoHeader []byte = nil
serializedMsg, err := pk.Marshal(pseudoHeader)
if err != nil {
return errors.Wrap(err, "Failed to encode ICMP message")
}
// The address needs to be of type UDPAddr when conn is created without priviledge
_, err = f.conn.WriteTo(serializedMsg, &net.UDPAddr{
IP: pk.Dst.AsSlice(),
})
return err
}
func (f *icmpFlow) listenResponse() error {
buf := make([]byte, mtu)
encoder := packet.NewEncoder()
for {
n, src, err := f.conn.ReadFrom(buf)
if err != nil {
return err
}
f.updateLastActive()
if err := f.handleResponse(encoder, src, buf[:n]); err != nil {
f.logger.Err(err).Str("dst", src.String()).Msg("Failed to handle ICMP response")
continue
}
}
}
func (f *icmpFlow) handleResponse(encoder *packet.Encoder, from net.Addr, rawPacket []byte) error {
// TODO: TUN-6654 Check for IPv6
msg, err := icmp.ParseMessage(int(layers.IPProtocolICMPv4), rawPacket)
if err != nil {
return err
}
echo, ok := msg.Body.(*icmp.Echo)
if !ok {
return fmt.Errorf("received unexpected icmp type %s from non-privileged ICMP socket", msg.Type)
}
addrPort, err := netip.ParseAddrPort(from.String())
if err != nil {
return err
}
icmpPacket := packet.ICMP{
IP: &packet.IP{
Src: addrPort.Addr(),
Dst: f.flow.Src,
Protocol: layers.IPProtocol(msg.Type.Protocol()),
},
Message: &icmp.Message{
Type: msg.Type,
Code: msg.Code,
Body: &icmp.Echo{
ID: int(f.echoID),
Seq: echo.Seq,
Data: echo.Data,
},
},
}
serializedPacket, err := encoder.Encode(&icmpPacket)
if err != nil {
return errors.Wrap(err, "Failed to encode ICMP message")
}
if err := f.flow.Responder.SendPacket(serializedPacket); err != nil {
return errors.Wrap(err, "Failed to send packet to the edge")
}
return nil
}
func (f *icmpFlow) updateLastActive() {
atomic.StoreInt64(&f.lastActive, time.Now().Unix())
}
+52
View File
@@ -0,0 +1,52 @@
//go:build linux
package ingress
import (
"errors"
"net"
"net/netip"
"testing"
"time"
"github.com/stretchr/testify/require"
"github.com/cloudflare/cloudflared/packet"
)
func TestCloseIdleFlow(t *testing.T) {
const (
echoID = 19234
idleTimeout = time.Millisecond * 100
)
conn, err := newICMPConn(localhostIP)
require.NoError(t, err)
flow := packet.Flow{
Src: netip.MustParseAddr("172.16.0.1"),
}
icmpFlow := newICMPFlow(conn, &flow, echoID, &noopLogger)
shutdownC := make(chan struct{})
flowErr := make(chan error)
go func() {
flowErr <- icmpFlow.serve(shutdownC, idleTimeout)
}()
require.Equal(t, errFlowInactive, <-flowErr)
}
func TestCloseConnStopFlow(t *testing.T) {
const (
echoID = 19234
)
conn, err := newICMPConn(localhostIP)
require.NoError(t, err)
flow := packet.Flow{
Src: netip.MustParseAddr("172.16.0.1"),
}
icmpFlow := newICMPFlow(conn, &flow, echoID, &noopLogger)
shutdownC := make(chan struct{})
conn.Close()
err = icmpFlow.serve(shutdownC, defaultCloseAfterIdle)
require.True(t, errors.Is(err, net.ErrClosed))
}
+6 -3
View File
@@ -182,11 +182,14 @@ func validateIngress(ingress []config.UnvalidatedIngressRule, defaults OriginReq
path := strings.TrimPrefix(r.Service, prefix)
service = &unixSocketPath{path: path, scheme: "https"}
} else if prefix := "http_status:"; strings.HasPrefix(r.Service, prefix) {
status, err := strconv.Atoi(strings.TrimPrefix(r.Service, prefix))
statusCode, err := strconv.Atoi(strings.TrimPrefix(r.Service, prefix))
if err != nil {
return Ingress{}, errors.Wrap(err, "invalid HTTP status")
return Ingress{}, errors.Wrap(err, "invalid HTTP status code")
}
srv := newStatusCode(status)
if statusCode < 100 || statusCode > 999 {
return Ingress{}, fmt.Errorf("invalid HTTP status code: %d", statusCode)
}
srv := newStatusCode(statusCode)
service = &srv
} else if r.Service == HelloWorldService || r.Service == "hello-world" || r.Service == "helloworld" {
service = new(helloWorld)
+8
View File
@@ -208,6 +208,14 @@ ingress:
args: args{rawYAML: `
ingress:
- service: http_status:asdf
`},
wantErr: true,
},
{
name: "Invalid HTTP status code",
args: args{rawYAML: `
ingress:
- service: http_status:8080
`},
wantErr: true,
},
+44
View File
@@ -0,0 +1,44 @@
package ingress
import (
"context"
"fmt"
"net/netip"
"time"
"github.com/rs/zerolog"
"golang.org/x/net/icmp"
"github.com/cloudflare/cloudflared/packet"
)
const (
defaultCloseAfterIdle = time.Second * 15
mtu = 1500
)
var (
errFlowInactive = fmt.Errorf("flow is inactive")
errPacketNil = fmt.Errorf("packet is nil")
)
// ICMPProxy sends ICMP messages and listens for their responses
type ICMPProxy interface {
// Serve starts listening for responses to the requests until context is done
Serve(ctx context.Context) error
// Request sends an ICMP message
Request(pk *packet.ICMP, responder packet.FlowResponder) error
}
func NewICMPProxy(listenIP netip.Addr, logger *zerolog.Logger) (ICMPProxy, error) {
return newICMPProxy(listenIP, logger)
}
// Opens a non-privileged ICMP socket on Linux and Darwin
func newICMPConn(listenIP netip.Addr) (*icmp.PacketConn, error) {
network := "udp6"
if listenIP.Is4() {
network = "udp4"
}
return icmp.ListenPacket(network, listenIP.String())
}
+150
View File
@@ -0,0 +1,150 @@
package ingress
import (
"context"
"fmt"
"net/netip"
"runtime"
"testing"
"github.com/google/gopacket/layers"
"github.com/rs/zerolog"
"github.com/stretchr/testify/require"
"golang.org/x/net/icmp"
"golang.org/x/net/ipv4"
"github.com/cloudflare/cloudflared/packet"
)
var (
noopLogger = zerolog.Nop()
localhostIP = netip.MustParseAddr("127.0.0.1")
)
// TestICMPProxyEcho makes sure we can send ICMP echo via the Request method and receives response via the
// ListenResponse method
func TestICMPProxyEcho(t *testing.T) {
onlyDarwinOrLinux(t)
const (
echoID = 36571
endSeq = 100
)
proxy, err := NewICMPProxy(localhostIP, &noopLogger)
require.NoError(t, err)
proxyDone := make(chan struct{})
ctx, cancel := context.WithCancel(context.Background())
go func() {
proxy.Serve(ctx)
close(proxyDone)
}()
responder := echoFlowResponder{
decoder: packet.NewICMPDecoder(),
respChan: make(chan []byte),
}
ip := packet.IP{
Src: localhostIP,
Dst: localhostIP,
Protocol: layers.IPProtocolICMPv4,
}
for i := 0; i < endSeq; i++ {
pk := packet.ICMP{
IP: &ip,
Message: &icmp.Message{
Type: ipv4.ICMPTypeEcho,
Code: 0,
Body: &icmp.Echo{
ID: echoID,
Seq: i,
Data: []byte(fmt.Sprintf("icmp echo seq %d", i)),
},
},
}
require.NoError(t, proxy.Request(&pk, &responder))
responder.validate(t, &pk)
}
cancel()
<-proxyDone
}
// TestICMPProxyRejectNotEcho makes sure it rejects messages other than echo
func TestICMPProxyRejectNotEcho(t *testing.T) {
onlyDarwinOrLinux(t)
msgs := []icmp.Message{
{
Type: ipv4.ICMPTypeDestinationUnreachable,
Code: 1,
Body: &icmp.DstUnreach{
Data: []byte("original packet"),
},
},
{
Type: ipv4.ICMPTypeTimeExceeded,
Code: 1,
Body: &icmp.TimeExceeded{
Data: []byte("original packet"),
},
},
{
Type: ipv4.ICMPType(2),
Code: 0,
Body: &icmp.PacketTooBig{
MTU: 1280,
Data: []byte("original packet"),
},
},
}
proxy, err := NewICMPProxy(localhostIP, &noopLogger)
require.NoError(t, err)
responder := echoFlowResponder{
decoder: packet.NewICMPDecoder(),
respChan: make(chan []byte),
}
for _, m := range msgs {
pk := packet.ICMP{
IP: &packet.IP{
Src: localhostIP,
Dst: localhostIP,
Protocol: layers.IPProtocolICMPv4,
},
Message: &m,
}
require.Error(t, proxy.Request(&pk, &responder))
}
}
func onlyDarwinOrLinux(t *testing.T) {
if runtime.GOOS != "darwin" && runtime.GOOS != "linux" {
t.Skip("Cannot create non-privileged datagram-oriented ICMP endpoint on Windows")
}
}
type echoFlowResponder struct {
decoder *packet.ICMPDecoder
respChan chan []byte
}
func (efr *echoFlowResponder) SendPacket(pk packet.RawPacket) error {
copiedPacket := make([]byte, len(pk.Data))
copy(copiedPacket, pk.Data)
efr.respChan <- copiedPacket
return nil
}
func (efr *echoFlowResponder) validate(t *testing.T, echoReq *packet.ICMP) {
pk := <-efr.respChan
decoded, err := efr.decoder.Decode(packet.RawPacket{Data: pk})
require.NoError(t, err)
require.Equal(t, decoded.Src, echoReq.Dst)
require.Equal(t, decoded.Dst, echoReq.Src)
require.Equal(t, echoReq.Protocol, decoded.Protocol)
require.Equal(t, ipv4.ICMPTypeEchoReply, decoded.Type)
require.Equal(t, 0, decoded.Code)
require.NotZero(t, decoded.Checksum)
require.Equal(t, echoReq.Body, decoded.Body)
}
+2 -1
View File
@@ -500,7 +500,8 @@ func TestClosePreviousProxies(t *testing.T) {
require.NoError(t, err)
require.Equal(t, http.StatusTeapot, resp.StatusCode)
// The hello-world server in config v1 should have been stopped
// The hello-world server in config v1 should have been stopped. We wait a bit since it's closed asynchronously.
time.Sleep(time.Millisecond * 10)
resp, err = proxyHTTP(originProxyV1, hostname)
require.Error(t, err)
require.Nil(t, resp)
+184
View File
@@ -0,0 +1,184 @@
package packet
import (
"fmt"
"github.com/google/gopacket"
"github.com/google/gopacket/layers"
"github.com/pkg/errors"
"golang.org/x/net/icmp"
)
func FindProtocol(p []byte) (layers.IPProtocol, error) {
version, err := FindIPVersion(p)
if err != nil {
return 0, err
}
switch version {
case 4:
if len(p) < ipv4HeaderLen {
return 0, fmt.Errorf("IPv4 packet should have at least %d bytes, got %d bytes", ipv4HeaderLen, len(p))
}
// Protocol is in the 10th byte of IPv4 header
return layers.IPProtocol(p[9]), nil
case 6:
if len(p) < ipv6HeaderLen {
return 0, fmt.Errorf("IPv6 packet should have at least %d bytes, got %d bytes", ipv6HeaderLen, len(p))
}
// Next header is in the 7th byte of IPv6 header
return layers.IPProtocol(p[6]), nil
default:
return 0, fmt.Errorf("unknow ip version %d", version)
}
}
func FindIPVersion(p []byte) (uint8, error) {
if len(p) == 0 {
return 0, fmt.Errorf("packet length is 0")
}
return p[0] >> 4, nil
}
// IPDecoder decodes raw packets into IP. It can process packets sequentially without allocating
// memory for the layers, so it cannot be called concurrently.
type IPDecoder struct {
ipv4 *layers.IPv4
ipv6 *layers.IPv6
layers uint8
v4parser *gopacket.DecodingLayerParser
v6parser *gopacket.DecodingLayerParser
}
func NewIPDecoder() *IPDecoder {
var (
ipv4 layers.IPv4
ipv6 layers.IPv6
)
dlpv4 := gopacket.NewDecodingLayerParser(layers.LayerTypeIPv4)
dlpv4.SetDecodingLayerContainer(gopacket.DecodingLayerSparse(nil))
dlpv4.AddDecodingLayer(&ipv4)
// Stop parsing when it encounter a layer that it doesn't have a parser
dlpv4.IgnoreUnsupported = true
dlpv6 := gopacket.NewDecodingLayerParser(layers.LayerTypeIPv6)
dlpv6.SetDecodingLayerContainer(gopacket.DecodingLayerSparse(nil))
dlpv6.AddDecodingLayer(&ipv6)
dlpv6.IgnoreUnsupported = true
return &IPDecoder{
ipv4: &ipv4,
ipv6: &ipv6,
layers: 1,
v4parser: dlpv4,
v6parser: dlpv6,
}
}
func (pd *IPDecoder) Decode(packet RawPacket) (*IP, error) {
// Should decode to IP layer
decoded, err := pd.decodeByVersion(packet.Data)
if err != nil {
return nil, err
}
for _, layerType := range decoded {
switch layerType {
case layers.LayerTypeIPv4:
return newIPv4(pd.ipv4)
case layers.LayerTypeIPv6:
return newIPv6(pd.ipv6)
}
}
return nil, fmt.Errorf("no ip layer is decoded")
}
func (pd *IPDecoder) decodeByVersion(packet []byte) ([]gopacket.LayerType, error) {
version, err := FindIPVersion(packet)
if err != nil {
return nil, err
}
decoded := make([]gopacket.LayerType, 0, pd.layers)
switch version {
case 4:
err = pd.v4parser.DecodeLayers(packet, &decoded)
case 6:
err = pd.v6parser.DecodeLayers(packet, &decoded)
default:
err = fmt.Errorf("unknow ip version %d", version)
}
if err != nil {
return nil, err
}
return decoded, nil
}
// ICMPDecoder decodes raw packets into IP and ICMP. It can process packets sequentially without allocating
// memory for the layers, so it cannot be called concurrently.
type ICMPDecoder struct {
*IPDecoder
icmpv4 *layers.ICMPv4
icmpv6 *layers.ICMPv6
}
func NewICMPDecoder() *ICMPDecoder {
ipDecoder := NewIPDecoder()
var (
icmpv4 layers.ICMPv4
icmpv6 layers.ICMPv6
)
ipDecoder.layers++
ipDecoder.v4parser.AddDecodingLayer(&icmpv4)
ipDecoder.v6parser.AddDecodingLayer(&icmpv6)
return &ICMPDecoder{
IPDecoder: ipDecoder,
icmpv4: &icmpv4,
icmpv6: &icmpv6,
}
}
func (pd *ICMPDecoder) Decode(packet RawPacket) (*ICMP, error) {
// Should decode to IP and optionally ICMP layer
decoded, err := pd.decodeByVersion(packet.Data)
if err != nil {
return nil, err
}
for _, layerType := range decoded {
switch layerType {
case layers.LayerTypeICMPv4:
ipv4, err := newIPv4(pd.ipv4)
if err != nil {
return nil, err
}
msg, err := icmp.ParseMessage(int(layers.IPProtocolICMPv4), append(pd.icmpv4.Contents, pd.icmpv4.Payload...))
if err != nil {
return nil, errors.Wrap(err, "failed to parse ICMPv4 message")
}
return &ICMP{
IP: ipv4,
Message: msg,
}, nil
case layers.LayerTypeICMPv6:
ipv6, err := newIPv6(pd.ipv6)
if err != nil {
return nil, err
}
msg, err := icmp.ParseMessage(int(layers.IPProtocolICMPv6), append(pd.icmpv6.Contents, pd.icmpv6.Payload...))
if err != nil {
return nil, errors.Wrap(err, "failed to parse ICMPv6")
}
return &ICMP{
IP: ipv6,
Message: msg,
}, nil
}
}
layers := make([]string, len(decoded))
for i, l := range decoded {
layers[i] = l.String()
}
return nil, fmt.Errorf("Expect to decode IP and ICMP layers, got %s", layers)
}
+252
View File
@@ -0,0 +1,252 @@
package packet
import (
"net"
"net/netip"
"testing"
"github.com/google/gopacket"
"github.com/google/gopacket/layers"
"github.com/stretchr/testify/require"
"golang.org/x/net/icmp"
"golang.org/x/net/ipv4"
"golang.org/x/net/ipv6"
)
func TestDecodeIP(t *testing.T) {
ipDecoder := NewIPDecoder()
icmpDecoder := NewICMPDecoder()
udps := []UDP{
{
IP: IP{
Src: netip.MustParseAddr("172.16.0.1"),
Dst: netip.MustParseAddr("10.0.0.1"),
Protocol: layers.IPProtocolUDP,
},
SrcPort: 31678,
DstPort: 53,
},
{
IP: IP{
Src: netip.MustParseAddr("fd51:2391:523:f4ee::1"),
Dst: netip.MustParseAddr("fd51:2391:697:f4ee::2"),
Protocol: layers.IPProtocolUDP,
},
SrcPort: 52139,
DstPort: 1053,
},
}
encoder := NewEncoder()
for _, udp := range udps {
p, err := encoder.Encode(&udp)
require.NoError(t, err)
ipPacket, err := ipDecoder.Decode(p)
require.NoError(t, err)
assertIPLayer(t, &udp.IP, ipPacket)
icmpPacket, err := icmpDecoder.Decode(p)
require.Error(t, err)
require.Nil(t, icmpPacket)
}
}
func TestDecodeICMP(t *testing.T) {
ipDecoder := NewIPDecoder()
icmpDecoder := NewICMPDecoder()
var (
ipv4Packet = IP{
Src: netip.MustParseAddr("172.16.0.1"),
Dst: netip.MustParseAddr("10.0.0.1"),
Protocol: layers.IPProtocolICMPv4,
}
ipv6Packet = IP{
Src: netip.MustParseAddr("fd51:2391:523:f4ee::1"),
Dst: netip.MustParseAddr("fd51:2391:697:f4ee::2"),
Protocol: layers.IPProtocolICMPv6,
}
icmpID = 100
icmpSeq = 52819
)
tests := []struct {
testCase string
packet *ICMP
}{
{
testCase: "icmpv4 time exceed",
packet: &ICMP{
IP: &ipv4Packet,
Message: &icmp.Message{
Type: ipv4.ICMPTypeTimeExceeded,
Code: 0,
Body: &icmp.TimeExceeded{
Data: []byte("original packet"),
},
},
},
},
{
testCase: "icmpv4 echo",
packet: &ICMP{
IP: &ipv4Packet,
Message: &icmp.Message{
Type: ipv4.ICMPTypeEcho,
Code: 0,
Body: &icmp.Echo{
ID: icmpID,
Seq: icmpSeq,
Data: []byte("icmpv4 echo"),
},
},
},
},
{
testCase: "icmpv6 destination unreachable",
packet: &ICMP{
IP: &ipv6Packet,
Message: &icmp.Message{
Type: ipv6.ICMPTypeDestinationUnreachable,
Code: 4,
Body: &icmp.DstUnreach{
Data: []byte("original packet"),
},
},
},
},
{
testCase: "icmpv6 echo",
packet: &ICMP{
IP: &ipv6Packet,
Message: &icmp.Message{
Type: ipv6.ICMPTypeEchoRequest,
Code: 0,
Body: &icmp.Echo{
ID: icmpID,
Seq: icmpSeq,
Data: []byte("icmpv6 echo"),
},
},
},
},
}
encoder := NewEncoder()
for _, test := range tests {
p, err := encoder.Encode(test.packet)
require.NoError(t, err)
ipPacket, err := ipDecoder.Decode(p)
require.NoError(t, err)
if ipPacket.Src.Is4() {
assertIPLayer(t, &ipv4Packet, ipPacket)
} else {
assertIPLayer(t, &ipv6Packet, ipPacket)
}
icmpPacket, err := icmpDecoder.Decode(p)
require.NoError(t, err)
require.Equal(t, ipPacket, icmpPacket.IP)
require.Equal(t, test.packet.Type, icmpPacket.Type)
require.Equal(t, test.packet.Code, icmpPacket.Code)
require.Equal(t, test.packet.Body, icmpPacket.Body)
expectedBody, err := test.packet.Body.Marshal(test.packet.Type.Protocol())
require.NoError(t, err)
decodedBody, err := icmpPacket.Body.Marshal(test.packet.Type.Protocol())
require.NoError(t, err)
require.Equal(t, expectedBody, decodedBody)
}
}
// TestDecodeBadPackets makes sure decoders don't decode invalid packets
func TestDecodeBadPackets(t *testing.T) {
var (
srcIPv4 = net.ParseIP("172.16.0.1")
dstIPv4 = net.ParseIP("10.0.0.1")
)
ipLayer := layers.IPv4{
Version: 10,
SrcIP: srcIPv4,
DstIP: dstIPv4,
Protocol: layers.IPProtocolICMPv4,
TTL: defaultTTL,
}
icmpLayer := layers.ICMPv4{
TypeCode: layers.CreateICMPv4TypeCode(uint8(ipv4.ICMPTypeEcho), 0),
Id: 100,
Seq: 52819,
}
wrongIPVersion, err := createPacket(&ipLayer, &icmpLayer, nil, nil)
require.NoError(t, err)
tests := []struct {
testCase string
packet []byte
}{
{
testCase: "unknown IP version",
packet: wrongIPVersion,
},
{
testCase: "invalid packet",
packet: []byte("not a packet"),
},
{
testCase: "zero length packet",
packet: []byte{},
},
}
ipDecoder := NewIPDecoder()
icmpDecoder := NewICMPDecoder()
for _, test := range tests {
ipPacket, err := ipDecoder.Decode(RawPacket{Data: test.packet})
require.Error(t, err)
require.Nil(t, ipPacket)
icmpPacket, err := icmpDecoder.Decode(RawPacket{Data: test.packet})
require.Error(t, err)
require.Nil(t, icmpPacket)
}
}
func createPacket(ipLayer, secondLayer, thirdLayer gopacket.SerializableLayer, body []byte) ([]byte, error) {
payload := gopacket.Payload(body)
packet := gopacket.NewSerializeBuffer()
var err error
if thirdLayer != nil {
err = gopacket.SerializeLayers(packet, serializeOpts, ipLayer, secondLayer, thirdLayer, payload)
} else {
err = gopacket.SerializeLayers(packet, serializeOpts, ipLayer, secondLayer, payload)
}
if err != nil {
return nil, err
}
return packet.Bytes(), nil
}
func assertIPLayer(t *testing.T, expected, actual *IP) {
require.Equal(t, expected.Src, actual.Src)
require.Equal(t, expected.Dst, actual.Dst)
require.Equal(t, expected.Protocol, actual.Protocol)
}
type UDP struct {
IP
SrcPort, DstPort layers.UDPPort
}
func (u *UDP) EncodeLayers() ([]gopacket.SerializableLayer, error) {
ipLayers, err := u.IP.EncodeLayers()
if err != nil {
return nil, err
}
udpLayer := layers.UDP{
SrcPort: u.SrcPort,
DstPort: u.DstPort,
}
udpLayer.SetNetworkLayerForChecksum(ipLayers[0].(gopacket.NetworkLayer))
return append(ipLayers, &udpLayer), nil
}
+39
View File
@@ -0,0 +1,39 @@
package packet
import "github.com/google/gopacket"
var (
serializeOpts = gopacket.SerializeOptions{
FixLengths: true,
ComputeChecksums: true,
}
)
// RawPacket represents a raw packet or one encoded by Encoder
type RawPacket struct {
Data []byte
}
type Encoder struct {
// buf is reusable because SerializeLayers calls the Clear method before each encoding
buf gopacket.SerializeBuffer
}
func NewEncoder() *Encoder {
return &Encoder{
buf: gopacket.NewSerializeBuffer(),
}
}
func (e Encoder) Encode(packet Packet) (RawPacket, error) {
encodedLayers, err := packet.EncodeLayers()
if err != nil {
return RawPacket{}, err
}
if err := gopacket.SerializeLayers(e.buf, serializeOpts, encodedLayers...); err != nil {
return RawPacket{}, err
}
return RawPacket{
Data: e.buf.Bytes(),
}, nil
}
+94
View File
@@ -0,0 +1,94 @@
package packet
import (
"errors"
"fmt"
"net/netip"
"sync"
)
var (
ErrFlowNotFound = errors.New("flow not found")
)
// FlowID represents a key type that can be used by FlowTracker
type FlowID interface {
// Type returns the name of the type that implements the FlowID
Type() string
fmt.Stringer
}
type Flow struct {
Src netip.Addr
Dst netip.Addr
Responder FlowResponder
}
func isSameFlow(f1, f2 *Flow) bool {
if f1 == nil || f2 == nil {
return false
}
return *f1 == *f2
}
// FlowResponder sends response packets to the flow
type FlowResponder interface {
// SendPacket returns a packet to the flow. It must not modify the packet,
// and after return it must not read the packet
SendPacket(pk RawPacket) error
}
// FlowTracker tracks flow from the perspective of eyeball to origin
type FlowTracker struct {
lock sync.RWMutex
flows map[FlowID]*Flow
}
func NewFlowTracker() *FlowTracker {
return &FlowTracker{
flows: make(map[FlowID]*Flow),
}
}
func (sft *FlowTracker) Get(id FlowID) (*Flow, bool) {
sft.lock.RLock()
defer sft.lock.RUnlock()
flow, ok := sft.flows[id]
return flow, ok
}
// Registers a flow. If shouldReplace = true, replace the current flow
func (sft *FlowTracker) Register(id FlowID, flow *Flow, shouldReplace bool) (replaced bool) {
sft.lock.Lock()
defer sft.lock.Unlock()
currentFlow, ok := sft.flows[id]
if !ok {
sft.flows[id] = flow
return false
}
if shouldReplace && !isSameFlow(currentFlow, flow) {
sft.flows[id] = flow
return true
}
return false
}
// Unregisters a flow. If force = true, delete it even if it maps to a different flow
func (sft *FlowTracker) Unregister(id FlowID, flow *Flow, force bool) (forceDeleted bool) {
sft.lock.Lock()
defer sft.lock.Unlock()
currentFlow, ok := sft.flows[id]
if !ok {
return false
}
if isSameFlow(currentFlow, flow) {
delete(sft.flows, id)
return false
}
if force {
delete(sft.flows, id)
return true
}
return false
}
+115
View File
@@ -0,0 +1,115 @@
package packet
import (
"fmt"
"net/netip"
"github.com/google/gopacket"
"github.com/google/gopacket/layers"
"golang.org/x/net/icmp"
)
const (
defaultTTL uint8 = 64
ipv4HeaderLen = 20
ipv6HeaderLen = 40
)
// Packet represents an IP packet or a packet that is encapsulated by IP
type Packet interface {
// IPLayer returns the IP of the packet
IPLayer() *IP
// EncodeLayers returns the layers that make up this packet. They can be passed to an Encoder to serialize into RawPacket
EncodeLayers() ([]gopacket.SerializableLayer, error)
}
// IP represents a generic IP packet. It can be embedded in more specific IP protocols
type IP struct {
Src netip.Addr
Dst netip.Addr
Protocol layers.IPProtocol
}
func newIPv4(ipLayer *layers.IPv4) (*IP, error) {
src, ok := netip.AddrFromSlice(ipLayer.SrcIP)
if !ok {
return nil, fmt.Errorf("cannot convert source IP %s to netip.Addr", ipLayer.SrcIP)
}
dst, ok := netip.AddrFromSlice(ipLayer.DstIP)
if !ok {
return nil, fmt.Errorf("cannot convert source IP %s to netip.Addr", ipLayer.DstIP)
}
return &IP{
Src: src,
Dst: dst,
Protocol: ipLayer.Protocol,
}, nil
}
func newIPv6(ipLayer *layers.IPv6) (*IP, error) {
src, ok := netip.AddrFromSlice(ipLayer.SrcIP)
if !ok {
return nil, fmt.Errorf("cannot convert source IP %s to netip.Addr", ipLayer.SrcIP)
}
dst, ok := netip.AddrFromSlice(ipLayer.DstIP)
if !ok {
return nil, fmt.Errorf("cannot convert source IP %s to netip.Addr", ipLayer.DstIP)
}
return &IP{
Src: src,
Dst: dst,
Protocol: ipLayer.NextHeader,
}, nil
}
func (ip *IP) IPLayer() *IP {
return ip
}
func (ip *IP) isIPv4() bool {
return ip.Src.Is4()
}
func (ip *IP) EncodeLayers() ([]gopacket.SerializableLayer, error) {
if ip.isIPv4() {
return []gopacket.SerializableLayer{
&layers.IPv4{
Version: 4,
SrcIP: ip.Src.AsSlice(),
DstIP: ip.Dst.AsSlice(),
Protocol: layers.IPProtocol(ip.Protocol),
TTL: defaultTTL,
},
}, nil
} else {
return []gopacket.SerializableLayer{
&layers.IPv6{
Version: 6,
SrcIP: ip.Src.AsSlice(),
DstIP: ip.Dst.AsSlice(),
NextHeader: layers.IPProtocol(ip.Protocol),
HopLimit: defaultTTL,
},
}, nil
}
}
// ICMP represents is an IP packet + ICMP message
type ICMP struct {
*IP
*icmp.Message
}
func (i *ICMP) EncodeLayers() ([]gopacket.SerializableLayer, error) {
ipLayers, err := i.IP.EncodeLayers()
if err != nil {
return nil, err
}
msg, err := i.Marshal(nil)
if err != nil {
return nil, err
}
icmpLayer := gopacket.Payload(msg)
return append(ipLayers, icmpLayer), nil
}
+8
View File
@@ -0,0 +1,8 @@
package packet
import "github.com/google/uuid"
type Session struct {
ID uuid.UUID
Payload []byte
}
+21 -33
View File
@@ -1,7 +1,6 @@
package proxy
import (
"bufio"
"context"
"fmt"
"io"
@@ -29,6 +28,8 @@ const (
LogFieldRule = "ingressRule"
LogFieldOriginService = "originService"
LogFieldFlowID = "flowID"
trailerHeaderName = "Trailer"
)
// Proxy represents a means to Proxy between cloudflared and the origin services.
@@ -207,15 +208,16 @@ func (p *Proxy) proxyHTTPRequest(
tracing.EndWithStatusCode(ttfbSpan, resp.StatusCode)
defer resp.Body.Close()
// resp headers can be nil
if resp.Header == nil {
resp.Header = make(http.Header)
headers := make(http.Header, len(resp.Header))
// copy headers
for k, v := range resp.Header {
headers[k] = v
}
// Add spans to response header (if available)
tr.AddSpans(resp.Header)
tr.AddSpans(headers)
err = w.WriteRespHeaders(resp.StatusCode, resp.Header)
err = w.WriteRespHeaders(resp.StatusCode, headers)
if err != nil {
return errors.Wrap(err, "Error writing response header")
}
@@ -236,12 +238,10 @@ func (p *Proxy) proxyHTTPRequest(
return nil
}
if connection.IsServerSentEvent(resp.Header) {
p.log.Debug().Msg("Detected Server-Side Events from Origin")
p.writeEventStream(w, resp.Body)
} else {
_, _ = cfio.Copy(w, resp.Body)
}
_, _ = cfio.Copy(w, resp.Body)
// copy trailers
copyTrailers(w, resp)
p.logOriginResponse(resp, fields)
return nil
@@ -256,7 +256,7 @@ func (p *Proxy) proxyStream(
connectionProxy ingress.StreamBasedOriginProxy,
) error {
ctx := tr.Context
_, connectSpan := tr.Tracer().Start(ctx, "stream_connect")
_, connectSpan := tr.Tracer().Start(ctx, "stream-connect")
originConn, err := connectionProxy.EstablishConnection(ctx, dest)
if err != nil {
tracing.EndWithErrorStatus(connectSpan, err)
@@ -296,26 +296,6 @@ func (wr *bidirectionalStream) Write(p []byte) (n int, err error) {
return wr.writer.Write(p)
}
func (p *Proxy) writeEventStream(w connection.ResponseWriter, respBody io.ReadCloser) {
reader := bufio.NewReader(respBody)
for {
line, readErr := reader.ReadBytes('\n')
// We first try to write whatever we read even if an error occurred
// The reason for doing it is to guarantee we really push everything to the eyeball side
// before returning
if len(line) > 0 {
if _, writeErr := w.Write(line); writeErr != nil {
return
}
}
if readErr != nil {
return
}
}
}
func (p *Proxy) appendTagHeaders(r *http.Request) {
for _, tag := range p.tags {
r.Header.Add(TagHeaderNamePrefix+tag.Name, tag.Value)
@@ -329,6 +309,14 @@ type logFields struct {
flowID string
}
func copyTrailers(w connection.ResponseWriter, response *http.Response) {
for trailerHeader, trailerValues := range response.Trailer {
for _, trailerValue := range trailerValues {
w.AddTrailer(trailerHeader, trailerValue)
}
}
}
func (p *Proxy) logRequest(r *http.Request, fields logFields) {
if fields.cfRay != "" {
p.log.Debug().Msgf("CF-RAY: %s %s %s %s", fields.cfRay, r.Method, r.URL, r.Proto)
+32 -15
View File
@@ -22,6 +22,8 @@ import (
"github.com/urfave/cli/v2"
"golang.org/x/sync/errgroup"
"github.com/cloudflare/cloudflared/cfio"
"github.com/cloudflare/cloudflared/config"
"github.com/cloudflare/cloudflared/connection"
"github.com/cloudflare/cloudflared/hello"
@@ -62,6 +64,10 @@ func (w *mockHTTPRespWriter) WriteRespHeaders(status int, header http.Header) er
return nil
}
func (w *mockHTTPRespWriter) AddTrailer(trailerName, trailerValue string) {
// do nothing
}
func (w *mockHTTPRespWriter) Read(data []byte) (int, error) {
return 0, fmt.Errorf("mockHTTPRespWriter doesn't implement io.Reader")
}
@@ -117,10 +123,17 @@ func newMockSSERespWriter() *mockSSERespWriter {
}
func (w *mockSSERespWriter) Write(data []byte) (int, error) {
w.writeNotification <- data
newData := make([]byte, len(data))
copy(newData, data)
w.writeNotification <- newData
return len(data), nil
}
func (w *mockSSERespWriter) WriteString(str string) (int, error) {
return w.Write([]byte(str))
}
func (w *mockSSERespWriter) ReadBytes() []byte {
return <-w.writeNotification
}
@@ -147,7 +160,6 @@ func TestProxySingleOrigin(t *testing.T) {
t.Run("testProxyHTTP", testProxyHTTP(proxy))
t.Run("testProxyWebsocket", testProxyWebsocket(proxy))
t.Run("testProxySSE", testProxySSE(proxy))
t.Run("testProxySSEAllData", testProxySSEAllData(proxy))
cancel()
}
@@ -256,11 +268,8 @@ func testProxySSE(proxy connection.OriginProxy) func(t *testing.T) {
for i := 0; i < pushCount; i++ {
line := responseWriter.ReadBytes()
expect := fmt.Sprintf("%d\n", i)
expect := fmt.Sprintf("%d\n\n", i)
require.Equal(t, []byte(expect), line, fmt.Sprintf("Expect to read %v, got %v", expect, line))
line = responseWriter.ReadBytes()
require.Equal(t, []byte("\n"), line, fmt.Sprintf("Expect to read '\n', got %v", line))
}
cancel()
@@ -270,17 +279,15 @@ func testProxySSE(proxy connection.OriginProxy) func(t *testing.T) {
// Regression test to guarantee that we always write the contents downstream even if EOF is reached without
// hitting the delimiter
func testProxySSEAllData(proxy *Proxy) func(t *testing.T) {
return func(t *testing.T) {
eyeballReader := io.NopCloser(strings.NewReader("data\r\r"))
responseWriter := newMockSSERespWriter()
func TestProxySSEAllData(t *testing.T) {
eyeballReader := io.NopCloser(strings.NewReader("data\r\r"))
responseWriter := newMockSSERespWriter()
// responseWriter uses an unbuffered channel, so we call in a different go-routine
go proxy.writeEventStream(responseWriter, eyeballReader)
// responseWriter uses an unbuffered channel, so we call in a different go-routine
go cfio.Copy(responseWriter, eyeballReader)
result := string(<-responseWriter.writeNotification)
require.Equal(t, "data\r\r", result)
}
result := string(<-responseWriter.writeNotification)
require.Equal(t, "data\r\r", result)
}
func TestProxyMultipleOrigins(t *testing.T) {
@@ -525,6 +532,7 @@ func TestConnections(t *testing.T) {
},
want: want{
message: []byte("echo-test2"),
headers: http.Header{},
},
},
{
@@ -544,6 +552,7 @@ func TestConnections(t *testing.T) {
message: []byte("echo-test3"),
// We expect no headers here because they are sent back via
// the stream.
headers: http.Header{},
},
},
{
@@ -823,6 +832,10 @@ func (w *wsRespWriter) WriteRespHeaders(status int, header http.Header) error {
return nil
}
func (w *wsRespWriter) AddTrailer(trailerName, trailerValue string) {
// do nothing
}
// respHeaders is a test function to read respHeaders
func (w *wsRespWriter) headers() http.Header {
// Removing indeterminstic header because it cannot be asserted.
@@ -850,6 +863,10 @@ func (m *mockTCPRespWriter) Write(p []byte) (n int, err error) {
return m.w.Write(p)
}
func (w *mockTCPRespWriter) AddTrailer(trailerName, trailerValue string) {
// do nothing
}
func (m *mockTCPRespWriter) WriteRespHeaders(status int, header http.Header) error {
m.responseHeaders = header
m.code = status
+13 -17
View File
@@ -8,21 +8,18 @@ import (
"github.com/lucas-clemente/quic-go"
"github.com/pkg/errors"
"github.com/rs/zerolog"
"github.com/cloudflare/cloudflared/packet"
)
const (
sessionIDLen = len(uuid.UUID{})
)
type SessionDatagram struct {
ID uuid.UUID
Payload []byte
}
type BaseDatagramMuxer interface {
// MuxSession suffix the session ID to the payload so the other end of the QUIC connection can demultiplex the
// payload from multiple datagram sessions
MuxSession(sessionID uuid.UUID, payload []byte) error
// SendToSession suffix the session ID to the payload so the other end of the QUIC connection can demultiplex the
// payload from multiple datagram sessions.
SendToSession(session *packet.Session) error
// ServeReceive starts a loop to receive datagrams from the QUIC connection
ServeReceive(ctx context.Context) error
}
@@ -30,10 +27,10 @@ type BaseDatagramMuxer interface {
type DatagramMuxer struct {
session quic.Connection
logger *zerolog.Logger
demuxChan chan<- *SessionDatagram
demuxChan chan<- *packet.Session
}
func NewDatagramMuxer(quicSession quic.Connection, log *zerolog.Logger, demuxChan chan<- *SessionDatagram) *DatagramMuxer {
func NewDatagramMuxer(quicSession quic.Connection, log *zerolog.Logger, demuxChan chan<- *packet.Session) *DatagramMuxer {
logger := log.With().Uint8("datagramVersion", 1).Logger()
return &DatagramMuxer{
session: quicSession,
@@ -47,13 +44,12 @@ func (dm *DatagramMuxer) mtu() int {
return maxDatagramPayloadSize
}
func (dm *DatagramMuxer) MuxSession(sessionID uuid.UUID, payload []byte) error {
if len(payload) > dm.mtu() {
// TODO: TUN-5302 return ICMP packet too big message
// drop packet for now, eventually reply with ICMP for PMTUD
return fmt.Errorf("origin UDP payload has %d bytes, which exceeds transport MTU %d", len(payload), dm.mtu())
func (dm *DatagramMuxer) SendToSession(session *packet.Session) error {
if len(session.Payload) > dm.mtu() {
packetTooBigDropped.Inc()
return fmt.Errorf("origin UDP payload has %d bytes, which exceeds transport MTU %d", len(session.Payload), dm.mtu())
}
payloadWithMetadata, err := suffixSessionID(sessionID, payload)
payloadWithMetadata, err := suffixSessionID(session.ID, session.Payload)
if err != nil {
return errors.Wrap(err, "Failed to suffix session ID to datagram, it will be dropped")
}
@@ -86,7 +82,7 @@ func (dm *DatagramMuxer) demux(ctx context.Context, msg []byte) error {
if err != nil {
return err
}
sessionDatagram := SessionDatagram{
sessionDatagram := packet.Session{
ID: sessionID,
Payload: payload,
}
+69 -50
View File
@@ -1,7 +1,6 @@
package quic
import (
"bytes"
"context"
"crypto/rand"
"crypto/rsa"
@@ -10,14 +9,20 @@ import (
"encoding/pem"
"fmt"
"math/big"
"net/netip"
"testing"
"time"
"github.com/google/gopacket/layers"
"github.com/google/uuid"
"github.com/lucas-clemente/quic-go"
"github.com/rs/zerolog"
"github.com/stretchr/testify/require"
"golang.org/x/net/icmp"
"golang.org/x/net/ipv4"
"golang.org/x/sync/errgroup"
"github.com/cloudflare/cloudflared/packet"
)
var (
@@ -57,7 +62,7 @@ func TestDatagram(t *testing.T) {
maxPayload := make([]byte, maxDatagramPayloadSize)
noPayloadSession := uuid.New()
maxPayloadSession := uuid.New()
sessionToPayload := []*SessionDatagram{
sessionToPayload := []*packet.Session{
{
ID: noPayloadSession,
Payload: make([]byte, 0),
@@ -67,15 +72,45 @@ func TestDatagram(t *testing.T) {
Payload: maxPayload,
},
}
flowPayloads := [][]byte{
maxPayload,
packets := []packet.ICMP{
{
IP: &packet.IP{
Src: netip.MustParseAddr("172.16.0.1"),
Dst: netip.MustParseAddr("192.168.0.1"),
Protocol: layers.IPProtocolICMPv4,
},
Message: &icmp.Message{
Type: ipv4.ICMPTypeTimeExceeded,
Code: 0,
Body: &icmp.TimeExceeded{
Data: []byte("original packet"),
},
},
},
{
IP: &packet.IP{
Src: netip.MustParseAddr("172.16.0.2"),
Dst: netip.MustParseAddr("192.168.0.2"),
Protocol: layers.IPProtocolICMPv4,
},
Message: &icmp.Message{
Type: ipv4.ICMPTypeEcho,
Code: 0,
Body: &icmp.Echo{
ID: 6182,
Seq: 9151,
Data: []byte("Test ICMP echo"),
},
},
},
}
testDatagram(t, 1, sessionToPayload, nil)
testDatagram(t, 2, sessionToPayload, flowPayloads)
testDatagram(t, 2, sessionToPayload, packets)
}
func testDatagram(t *testing.T, version uint8, sessionToPayloads []*SessionDatagram, packetPayloads [][]byte) {
func testDatagram(t *testing.T, version uint8, sessionToPayloads []*packet.Session, packets []packet.ICMP) {
quicConfig := &quic.Config{
KeepAlivePeriod: 5 * time.Millisecond,
EnableDatagrams: true,
@@ -95,19 +130,27 @@ func testDatagram(t *testing.T, version uint8, sessionToPayloads []*SessionDatag
return err
}
sessionDemuxChan := make(chan *SessionDatagram, 16)
sessionDemuxChan := make(chan *packet.Session, 16)
switch version {
case 1:
muxer := NewDatagramMuxer(quicSession, &logger, sessionDemuxChan)
muxer.ServeReceive(ctx)
case 2:
packetDemuxChan := make(chan []byte, len(packetPayloads))
muxer := NewDatagramMuxerV2(quicSession, &logger, sessionDemuxChan, packetDemuxChan)
muxer := NewDatagramMuxerV2(quicSession, &logger, sessionDemuxChan)
muxer.ServeReceive(ctx)
for _, expectedPayload := range packetPayloads {
require.Equal(t, expectedPayload, <-packetDemuxChan)
icmpDecoder := packet.NewICMPDecoder()
for _, pk := range packets {
received, err := muxer.ReceivePacket(ctx)
require.NoError(t, err)
receivedICMP, err := icmpDecoder.Decode(received)
require.NoError(t, err)
require.Equal(t, pk.IP, receivedICMP.IP)
require.Equal(t, pk.Type, receivedICMP.Type)
require.Equal(t, pk.Code, receivedICMP.Code)
require.Equal(t, pk.Body, receivedICMP.Body)
}
default:
return fmt.Errorf("unknown datagram version %d", version)
@@ -140,22 +183,30 @@ func testDatagram(t *testing.T, version uint8, sessionToPayloads []*SessionDatag
case 1:
muxer = NewDatagramMuxer(quicSession, &logger, nil)
case 2:
muxerV2 := NewDatagramMuxerV2(quicSession, &logger, nil, nil)
for _, payload := range packetPayloads {
require.NoError(t, muxerV2.MuxPacket(payload))
muxerV2 := NewDatagramMuxerV2(quicSession, &logger, nil)
encoder := packet.NewEncoder()
for _, pk := range packets {
encodedPacket, err := encoder.Encode(&pk)
require.NoError(t, err)
require.NoError(t, muxerV2.SendPacket(encodedPacket))
}
// Payload larger than transport MTU, should not be sent
require.Error(t, muxerV2.MuxPacket(largePayload))
require.Error(t, muxerV2.SendPacket(packet.RawPacket{
Data: largePayload,
}))
muxer = muxerV2
default:
return fmt.Errorf("unknown datagram version %d", version)
}
for _, sessionDatagram := range sessionToPayloads {
require.NoError(t, muxer.MuxSession(sessionDatagram.ID, sessionDatagram.Payload))
for _, session := range sessionToPayloads {
require.NoError(t, muxer.SendToSession(session))
}
// Payload larger than transport MTU, should not be sent
require.Error(t, muxer.MuxSession(testSessionID, largePayload))
require.Error(t, muxer.SendToSession(&packet.Session{
ID: testSessionID,
Payload: largePayload,
}))
// Wait for edge to finish receiving the messages
time.Sleep(time.Millisecond * 100)
@@ -198,35 +249,3 @@ func generateTLSConfig() *tls.Config {
NextProtos: []string{"argotunnel"},
}
}
type sessionMuxer interface {
SendToSession(sessionID uuid.UUID, payload []byte) error
}
type mockSessionReceiver struct {
expectedSessionToPayload map[uuid.UUID][]byte
receivedCount int
}
func (msr *mockSessionReceiver) ReceiveDatagram(sessionID uuid.UUID, payload []byte) error {
expectedPayload := msr.expectedSessionToPayload[sessionID]
if !bytes.Equal(expectedPayload, payload) {
return fmt.Errorf("expect %v to have payload %s, got %s", sessionID, string(expectedPayload), string(payload))
}
msr.receivedCount++
return nil
}
type mockFlowReceiver struct {
expectedPayloads [][]byte
receivedCount int
}
func (mfr *mockFlowReceiver) ReceiveFlow(payload []byte) error {
expectedPayload := mfr.expectedPayloads[mfr.receivedCount]
if !bytes.Equal(expectedPayload, payload) {
return fmt.Errorf("expect flow %d to have payload %s, got %s", mfr.receivedCount, string(expectedPayload), string(payload))
}
mfr.receivedCount++
return nil
}
+67 -41
View File
@@ -4,10 +4,11 @@ import (
"context"
"fmt"
"github.com/google/uuid"
"github.com/lucas-clemente/quic-go"
"github.com/pkg/errors"
"github.com/rs/zerolog"
"github.com/cloudflare/cloudflared/packet"
)
type datagramV2Type byte
@@ -17,8 +18,14 @@ const (
ip
)
const (
typeIDLen = 1
// Same as sessionDemuxChan capacity
packetChanCapacity = 16
)
func suffixType(b []byte, datagramType datagramV2Type) ([]byte, error) {
if len(b)+1 > MaxDatagramFrameSize {
if len(b)+typeIDLen > MaxDatagramFrameSize {
return nil, fmt.Errorf("datagram size %d exceeds max frame size %d", len(b), MaxDatagramFrameSize)
}
b = append(b, byte(datagramType))
@@ -33,32 +40,32 @@ func (dm *DatagramMuxerV2) mtu() int {
type DatagramMuxerV2 struct {
session quic.Connection
logger *zerolog.Logger
sessionDemuxChan chan<- *SessionDatagram
packetDemuxChan chan<- []byte
sessionDemuxChan chan<- *packet.Session
packetDemuxChan chan packet.RawPacket
}
func NewDatagramMuxerV2(
quicSession quic.Connection,
log *zerolog.Logger,
sessionDemuxChan chan<- *SessionDatagram,
packetDemuxChan chan<- []byte) *DatagramMuxerV2 {
sessionDemuxChan chan<- *packet.Session,
) *DatagramMuxerV2 {
logger := log.With().Uint8("datagramVersion", 2).Logger()
return &DatagramMuxerV2{
session: quicSession,
logger: &logger,
sessionDemuxChan: sessionDemuxChan,
packetDemuxChan: packetDemuxChan,
packetDemuxChan: make(chan packet.RawPacket, packetChanCapacity),
}
}
// MuxSession suffix the session ID and datagram version to the payload so the other end of the QUIC connection can
// SendToSession suffix the session ID and datagram version to the payload so the other end of the QUIC connection can
// demultiplex the payload from multiple datagram sessions
func (dm *DatagramMuxerV2) MuxSession(sessionID uuid.UUID, payload []byte) error {
if len(payload) > dm.mtu() {
// TODO: TUN-5302 return ICMP packet too big message
return fmt.Errorf("origin UDP payload has %d bytes, which exceeds transport MTU %d", len(payload), dm.mtu())
func (dm *DatagramMuxerV2) SendToSession(session *packet.Session) error {
if len(session.Payload) > dm.mtu() {
packetTooBigDropped.Inc()
return fmt.Errorf("origin UDP payload has %d bytes, which exceeds transport MTU %d", len(session.Payload), dm.mtu())
}
msgWithID, err := suffixSessionID(sessionID, payload)
msgWithID, err := suffixSessionID(session.ID, session.Payload)
if err != nil {
return errors.Wrap(err, "Failed to suffix session ID to datagram, it will be dropped")
}
@@ -72,10 +79,10 @@ func (dm *DatagramMuxerV2) MuxSession(sessionID uuid.UUID, payload []byte) error
return nil
}
// MuxPacket suffix the datagram type to the packet. The other end of the QUIC connection can demultiplex by parsing
// SendPacket suffix the datagram type to the packet. The other end of the QUIC connection can demultiplex by parsing
// the payload as IP and look at the source and destination.
func (dm *DatagramMuxerV2) MuxPacket(packet []byte) error {
payloadWithVersion, err := suffixType(packet, ip)
func (dm *DatagramMuxerV2) SendPacket(pk packet.RawPacket) error {
payloadWithVersion, err := suffixType(pk.Data, ip)
if err != nil {
return errors.Wrap(err, "Failed to suffix datagram type, it will be dropped")
}
@@ -101,36 +108,55 @@ func (dm *DatagramMuxerV2) ServeReceive(ctx context.Context) error {
}
}
func (dm *DatagramMuxerV2) demux(ctx context.Context, msgWithType []byte) error {
if len(msgWithType) < 1 {
return fmt.Errorf("QUIC datagram should have at least 1 byte")
func (dm *DatagramMuxerV2) ReceivePacket(ctx context.Context) (packet.RawPacket, error) {
select {
case <-ctx.Done():
return packet.RawPacket{}, ctx.Err()
case pk := <-dm.packetDemuxChan:
return pk, nil
}
msgType := datagramV2Type(msgWithType[len(msgWithType)-1])
msg := msgWithType[0 : len(msgWithType)-1]
}
func (dm *DatagramMuxerV2) demux(ctx context.Context, msgWithType []byte) error {
if len(msgWithType) < typeIDLen {
return fmt.Errorf("QUIC datagram should have at least %d byte", typeIDLen)
}
msgType := datagramV2Type(msgWithType[len(msgWithType)-typeIDLen])
msg := msgWithType[0 : len(msgWithType)-typeIDLen]
switch msgType {
case udp:
sessionID, payload, err := extractSessionID(msg)
if err != nil {
return err
}
sessionDatagram := SessionDatagram{
ID: sessionID,
Payload: payload,
}
select {
case dm.sessionDemuxChan <- &sessionDatagram:
return nil
case <-ctx.Done():
return ctx.Err()
}
return dm.handleSession(ctx, msg)
case ip:
select {
case dm.packetDemuxChan <- msg:
return nil
case <-ctx.Done():
return ctx.Err()
}
return dm.handlePacket(ctx, msg)
default:
return fmt.Errorf("Unexpected datagram type %d", msgType)
}
}
func (dm *DatagramMuxerV2) handleSession(ctx context.Context, session []byte) error {
sessionID, payload, err := extractSessionID(session)
if err != nil {
return err
}
sessionDatagram := packet.Session{
ID: sessionID,
Payload: payload,
}
select {
case dm.sessionDemuxChan <- &sessionDatagram:
return nil
case <-ctx.Done():
return ctx.Err()
}
}
func (dm *DatagramMuxerV2) handlePacket(ctx context.Context, pk []byte) error {
select {
case <-ctx.Done():
return ctx.Err()
case dm.packetDemuxChan <- packet.RawPacket{
Data: pk,
}:
return nil
}
}
+8
View File
@@ -144,6 +144,13 @@ var (
}
registerClient = sync.Once{}
registerServer = sync.Once{}
packetTooBigDropped = prometheus.NewCounter(prometheus.CounterOpts{
Namespace: namespace,
Subsystem: perspectiveString(logging.PerspectiveClient),
Name: "packet_too_big_dropped",
Help: "Count of packets received from origin that are too big to send to the edge and are dropped as a result",
})
)
// MetricsCollector abstracts the difference between client and server metrics from connTracer
@@ -264,6 +271,7 @@ func newClientCollector(index uint8) MetricsCollector {
clientMetrics.minRTT,
clientMetrics.latestRTT,
clientMetrics.smoothedRTT,
packetTooBigDropped,
)
})
return &clientCollector{
+1 -1
View File
@@ -7,5 +7,5 @@ const (
// 1220 is the default value https://github.com/lucas-clemente/quic-go/blob/84e03e59760ceee37359688871bb0688fcc4e98f/internal/protocol/params.go#L138
MaxDatagramFrameSize = 1220
// 3 more bytes are reserved at https://github.com/lucas-clemente/quic-go/blob/v0.24.0/internal/wire/datagram_frame.go#L61
maxDatagramPayloadSize = MaxDatagramFrameSize - 3 - sessionIDLen
maxDatagramPayloadSize = MaxDatagramFrameSize - 3 - sessionIDLen - typeIDLen
)
+40 -6
View File
@@ -4,6 +4,7 @@ import (
"context"
"errors"
"fmt"
"net/netip"
"strings"
"time"
@@ -15,6 +16,7 @@ import (
"github.com/cloudflare/cloudflared/edgediscovery"
"github.com/cloudflare/cloudflared/edgediscovery/allregions"
"github.com/cloudflare/cloudflared/h2mux"
"github.com/cloudflare/cloudflared/ingress"
"github.com/cloudflare/cloudflared/orchestration"
"github.com/cloudflare/cloudflared/retry"
"github.com/cloudflare/cloudflared/signal"
@@ -44,7 +46,7 @@ type Supervisor struct {
config *TunnelConfig
orchestrator *orchestration.Orchestrator
edgeIPs *edgediscovery.Edge
edgeTunnelServer EdgeTunnelServer
edgeTunnelServer *EdgeTunnelServer
tunnelErrors chan tunnelError
tunnelsConnecting map[int]chan struct{}
tunnelsProtocolFallback map[int]*protocolFallback
@@ -114,6 +116,19 @@ func NewSupervisor(config *TunnelConfig, orchestrator *orchestration.Orchestrato
gracefulShutdownC: gracefulShutdownC,
connAwareLogger: log,
}
if useDatagramV2(config) {
// TODO: TUN-6654 listen for IPv6 and decide if it should listen on specific IP
listenIP, err := netip.ParseAddr("0.0.0.0")
if err != nil {
return nil, err
}
icmpProxy, err := ingress.NewICMPProxy(listenIP, config.Log)
if err != nil {
log.Logger().Warn().Err(err).Msg("Failed to create icmp proxy, will continue to use datagram v1")
} else {
edgeTunnelServer.icmpProxy = icmpProxy
}
}
useReconnectToken := false
if config.ClassicTunnel != nil {
@@ -125,7 +140,7 @@ func NewSupervisor(config *TunnelConfig, orchestrator *orchestration.Orchestrato
config: config,
orchestrator: orchestrator,
edgeIPs: edgeIPs,
edgeTunnelServer: edgeTunnelServer,
edgeTunnelServer: &edgeTunnelServer,
tunnelErrors: make(chan tunnelError),
tunnelsConnecting: map[int]chan struct{}{},
tunnelsProtocolFallback: map[int]*protocolFallback{},
@@ -142,6 +157,14 @@ func (s *Supervisor) Run(
ctx context.Context,
connectedSignal *signal.Signal,
) error {
if s.edgeTunnelServer.icmpProxy != nil {
go func() {
if err := s.edgeTunnelServer.icmpProxy.Serve(ctx); err != nil {
s.log.Logger().Err(err).Msg("icmp proxy terminated")
}
}()
}
if err := s.initialize(ctx, connectedSignal); err != nil {
if err == errEarlyShutdown {
return nil
@@ -249,7 +272,7 @@ func (s *Supervisor) initialize(
s.config.HAConnections = availableAddrs
}
s.tunnelsProtocolFallback[0] = &protocolFallback{
retry.BackoffHandler{MaxRetries: s.config.Retries},
retry.BackoffHandler{MaxRetries: s.config.Retries, RetryForever: true},
s.config.ProtocolSelector.Current(),
false,
}
@@ -271,12 +294,11 @@ func (s *Supervisor) initialize(
// At least one successful connection, so start the rest
for i := 1; i < s.config.HAConnections; i++ {
s.tunnelsProtocolFallback[i] = &protocolFallback{
retry.BackoffHandler{MaxRetries: s.config.Retries},
retry.BackoffHandler{MaxRetries: s.config.Retries, RetryForever: true},
s.config.ProtocolSelector.Current(),
false,
}
ch := signal.New(make(chan struct{}))
go s.startTunnel(ctx, i, ch)
go s.startTunnel(ctx, i, s.newConnectedTunnelSignal(i))
time.Sleep(registrationInterval)
}
return nil
@@ -413,3 +435,15 @@ func (s *Supervisor) authenticate(ctx context.Context, numPreviousAttempts int)
registrationOptions.NumPreviousAttempts = uint8(numPreviousAttempts)
return rpcClient.Authenticate(ctx, s.config.ClassicTunnel, registrationOptions)
}
func useDatagramV2(config *TunnelConfig) bool {
if config.NamedTunnel == nil {
return false
}
for _, feature := range config.NamedTunnel.Client.Features {
if feature == FeatureDatagramV2 {
return true
}
}
return false
}
+53 -96
View File
@@ -20,6 +20,7 @@ import (
"github.com/cloudflare/cloudflared/edgediscovery"
"github.com/cloudflare/cloudflared/edgediscovery/allregions"
"github.com/cloudflare/cloudflared/h2mux"
"github.com/cloudflare/cloudflared/ingress"
"github.com/cloudflare/cloudflared/orchestration"
quicpogs "github.com/cloudflare/cloudflared/quic"
"github.com/cloudflare/cloudflared/retry"
@@ -193,11 +194,12 @@ type EdgeTunnelServer struct {
reconnectCh chan ReconnectSignal
gracefulShutdownC <-chan struct{}
tracker *tunnelstate.ConnTracker
icmpProxy ingress.ICMPProxy
connAwareLogger *ConnAwareLogger
}
func (e EdgeTunnelServer) Serve(ctx context.Context, connIndex uint8, protocolFallback *protocolFallback, connectedSignal *signal.Signal) error {
func (e *EdgeTunnelServer) Serve(ctx context.Context, connIndex uint8, protocolFallback *protocolFallback, connectedSignal *signal.Signal) error {
haConnections.Inc()
defer haConnections.Dec()
@@ -229,20 +231,14 @@ func (e EdgeTunnelServer) Serve(ctx context.Context, connIndex uint8, protocolFa
// to another protocol when a particular metal doesn't support new protocol
// Each connection can also have it's own IP version because individual connections might fallback
// to another IP version.
err, recoverable := ServeTunnel(
err, recoverable := e.serveTunnel(
ctx,
connLog,
e.credentialManager,
e.config,
e.orchestrator,
addr,
connIndex,
connectedFuse,
protocolFallback,
e.cloudflaredUUID,
e.reconnectCh,
protocolFallback.protocol,
e.gracefulShutdownC,
)
// If the connection is recoverable, we want to maintain the same IP
@@ -361,20 +357,14 @@ func selectNextProtocol(
// ServeTunnel runs a single tunnel connection, returns nil on graceful shutdown,
// on error returns a flag indicating if error can be retried
func ServeTunnel(
func (e *EdgeTunnelServer) serveTunnel(
ctx context.Context,
connLog *ConnAwareLogger,
credentialManager *reconnectCredentialManager,
config *TunnelConfig,
orchestrator *orchestration.Orchestrator,
addr *allregions.EdgeAddr,
connIndex uint8,
fuse *h2mux.BooleanFuse,
backoff *protocolFallback,
cloudflaredUUID uuid.UUID,
reconnectCh chan ReconnectSignal,
protocol connection.Protocol,
gracefulShutdownC <-chan struct{},
) (err error, recoverable bool) {
// Treat panics as recoverable errors
defer func() {
@@ -389,21 +379,15 @@ func ServeTunnel(
}
}()
defer config.Observer.SendDisconnect(connIndex)
err, recoverable = serveTunnel(
defer e.config.Observer.SendDisconnect(connIndex)
err, recoverable = e.serveConnection(
ctx,
connLog,
credentialManager,
config,
orchestrator,
addr,
connIndex,
fuse,
backoff,
cloudflaredUUID,
reconnectCh,
protocol,
gracefulShutdownC,
)
if err != nil {
@@ -416,7 +400,7 @@ func ServeTunnel(
connLog.ConnAwareLogger().Err(err).Msg("Register tunnel error from server side")
// Don't send registration error return from server to Sentry. They are
// logged on server side
if incidents := config.IncidentLookup.ActiveIncidents(); len(incidents) > 0 {
if incidents := e.config.IncidentLookup.ActiveIncidents(); len(incidents) > 0 {
connLog.ConnAwareLogger().Msg(activeIncidentsMsg(incidents))
}
return err.Cause, !err.Permanent
@@ -442,93 +426,73 @@ func ServeTunnel(
return nil, false
}
func serveTunnel(
func (e *EdgeTunnelServer) serveConnection(
ctx context.Context,
connLog *ConnAwareLogger,
credentialManager *reconnectCredentialManager,
config *TunnelConfig,
orchestrator *orchestration.Orchestrator,
addr *allregions.EdgeAddr,
connIndex uint8,
fuse *h2mux.BooleanFuse,
backoff *protocolFallback,
cloudflaredUUID uuid.UUID,
reconnectCh chan ReconnectSignal,
protocol connection.Protocol,
gracefulShutdownC <-chan struct{},
) (err error, recoverable bool) {
connectedFuse := &connectedFuse{
fuse: fuse,
backoff: backoff,
}
controlStream := connection.NewControlStream(
config.Observer,
e.config.Observer,
connectedFuse,
config.NamedTunnel,
e.config.NamedTunnel,
connIndex,
addr.UDP.IP,
nil,
gracefulShutdownC,
config.GracePeriod,
e.gracefulShutdownC,
e.config.GracePeriod,
protocol,
)
switch protocol {
case connection.QUIC, connection.QUICWarp:
connOptions := config.connectionOptions(addr.UDP.String(), uint8(backoff.Retries()))
return ServeQUIC(ctx,
connOptions := e.config.connectionOptions(addr.UDP.String(), uint8(backoff.Retries()))
return e.serveQUIC(ctx,
addr.UDP,
config,
orchestrator,
connLog,
connOptions,
controlStream,
connIndex,
reconnectCh,
gracefulShutdownC)
connIndex)
case connection.HTTP2, connection.HTTP2Warp:
edgeConn, err := edgediscovery.DialEdge(ctx, dialTimeout, config.EdgeTLSConfigs[protocol], addr.TCP)
edgeConn, err := edgediscovery.DialEdge(ctx, dialTimeout, e.config.EdgeTLSConfigs[protocol], addr.TCP)
if err != nil {
connLog.ConnAwareLogger().Err(err).Msg("Unable to establish connection with Cloudflare edge")
return err, true
}
connOptions := config.connectionOptions(edgeConn.LocalAddr().String(), uint8(backoff.Retries()))
if err := ServeHTTP2(
connOptions := e.config.connectionOptions(edgeConn.LocalAddr().String(), uint8(backoff.Retries()))
if err := e.serveHTTP2(
ctx,
connLog,
config,
orchestrator,
edgeConn,
connOptions,
controlStream,
connIndex,
gracefulShutdownC,
reconnectCh,
); err != nil {
return err, false
}
default:
edgeConn, err := edgediscovery.DialEdge(ctx, dialTimeout, config.EdgeTLSConfigs[protocol], addr.TCP)
edgeConn, err := edgediscovery.DialEdge(ctx, dialTimeout, e.config.EdgeTLSConfigs[protocol], addr.TCP)
if err != nil {
connLog.ConnAwareLogger().Err(err).Msg("Unable to establish connection with Cloudflare edge")
return err, true
}
if err := ServeH2mux(
if err := e.serveH2mux(
ctx,
connLog,
credentialManager,
config,
orchestrator,
edgeConn,
connIndex,
connectedFuse,
cloudflaredUUID,
reconnectCh,
gracefulShutdownC,
); err != nil {
return err, false
}
@@ -544,30 +508,24 @@ func (r unrecoverableError) Error() string {
return r.err.Error()
}
func ServeH2mux(
func (e *EdgeTunnelServer) serveH2mux(
ctx context.Context,
connLog *ConnAwareLogger,
credentialManager *reconnectCredentialManager,
config *TunnelConfig,
orchestrator *orchestration.Orchestrator,
edgeConn net.Conn,
connIndex uint8,
connectedFuse *connectedFuse,
cloudflaredUUID uuid.UUID,
reconnectCh chan ReconnectSignal,
gracefulShutdownC <-chan struct{},
) error {
connLog.Logger().Debug().Msgf("Connecting via h2mux")
// Returns error from parsing the origin URL or handshake errors
handler, err, recoverable := connection.NewH2muxConnection(
orchestrator,
config.GracePeriod,
config.MuxerConfig,
e.orchestrator,
e.config.GracePeriod,
e.config.MuxerConfig,
edgeConn,
connIndex,
config.Observer,
gracefulShutdownC,
config.Log,
e.config.Observer,
e.gracefulShutdownC,
e.config.Log,
)
if err != nil {
if !recoverable {
@@ -579,42 +537,44 @@ func ServeH2mux(
errGroup, serveCtx := errgroup.WithContext(ctx)
errGroup.Go(func() error {
if config.NamedTunnel != nil {
connOptions := config.connectionOptions(edgeConn.LocalAddr().String(), uint8(connectedFuse.backoff.Retries()))
return handler.ServeNamedTunnel(serveCtx, config.NamedTunnel, connOptions, connectedFuse)
if e.config.NamedTunnel != nil {
connOptions := e.config.connectionOptions(edgeConn.LocalAddr().String(), uint8(connectedFuse.backoff.Retries()))
return handler.ServeNamedTunnel(serveCtx, e.config.NamedTunnel, connOptions, connectedFuse)
}
registrationOptions := config.registrationOptions(connIndex, edgeConn.LocalAddr().String(), cloudflaredUUID)
return handler.ServeClassicTunnel(serveCtx, config.ClassicTunnel, credentialManager, registrationOptions, connectedFuse)
registrationOptions := e.config.registrationOptions(connIndex, edgeConn.LocalAddr().String(), e.cloudflaredUUID)
return handler.ServeClassicTunnel(serveCtx, e.config.ClassicTunnel, e.credentialManager, registrationOptions, connectedFuse)
})
errGroup.Go(func() error {
return listenReconnect(serveCtx, reconnectCh, gracefulShutdownC)
err := listenReconnect(serveCtx, e.reconnectCh, e.gracefulShutdownC)
if err != nil {
// forcefully break the connection (this is only used for testing)
// errgroup will return context canceled for the handler.ServeClassicTunnel
connLog.Logger().Debug().Msg("Forcefully breaking h2mux connection")
}
return err
})
return errGroup.Wait()
}
func ServeHTTP2(
func (e *EdgeTunnelServer) serveHTTP2(
ctx context.Context,
connLog *ConnAwareLogger,
config *TunnelConfig,
orchestrator *orchestration.Orchestrator,
tlsServerConn net.Conn,
connOptions *tunnelpogs.ConnectionOptions,
controlStreamHandler connection.ControlStreamHandler,
connIndex uint8,
gracefulShutdownC <-chan struct{},
reconnectCh chan ReconnectSignal,
) error {
connLog.Logger().Debug().Msgf("Connecting via http2")
h2conn := connection.NewHTTP2Connection(
tlsServerConn,
orchestrator,
e.orchestrator,
connOptions,
config.Observer,
e.config.Observer,
connIndex,
controlStreamHandler,
config.Log,
e.config.Log,
)
errGroup, serveCtx := errgroup.WithContext(ctx)
@@ -623,11 +583,11 @@ func ServeHTTP2(
})
errGroup.Go(func() error {
err := listenReconnect(serveCtx, reconnectCh, gracefulShutdownC)
err := listenReconnect(serveCtx, e.reconnectCh, e.gracefulShutdownC)
if err != nil {
// forcefully break the connection (this is only used for testing)
// errgroup will return context canceled for the h2conn.Serve
connLog.Logger().Debug().Msg("Forcefully breaking http2 connection")
_ = tlsServerConn.Close()
}
return err
})
@@ -635,19 +595,15 @@ func ServeHTTP2(
return errGroup.Wait()
}
func ServeQUIC(
func (e *EdgeTunnelServer) serveQUIC(
ctx context.Context,
edgeAddr *net.UDPAddr,
config *TunnelConfig,
orchestrator *orchestration.Orchestrator,
connLogger *ConnAwareLogger,
connOptions *tunnelpogs.ConnectionOptions,
controlStreamHandler connection.ControlStreamHandler,
connIndex uint8,
reconnectCh chan ReconnectSignal,
gracefulShutdownC <-chan struct{},
) (err error, recoverable bool) {
tlsConfig := config.EdgeTLSConfigs[connection.QUIC]
tlsConfig := e.config.EdgeTLSConfigs[connection.QUIC]
quicConfig := &quic.Config{
HandshakeIdleTimeout: quicpogs.HandshakeIdleTimeout,
MaxIdleTimeout: quicpogs.MaxIdleTimeout,
@@ -663,10 +619,11 @@ func ServeQUIC(
quicConfig,
edgeAddr,
tlsConfig,
orchestrator,
e.orchestrator,
connOptions,
controlStreamHandler,
connLogger.Logger())
connLogger.Logger(),
e.icmpProxy)
if err != nil {
connLogger.ConnAwareLogger().Err(err).Msgf("Failed to create new quic connection")
return err, true
@@ -682,11 +639,11 @@ func ServeQUIC(
})
errGroup.Go(func() error {
err := listenReconnect(serveCtx, reconnectCh, gracefulShutdownC)
err := listenReconnect(serveCtx, e.reconnectCh, e.gracefulShutdownC)
if err != nil {
// forcefully break the connection (this is only used for testing)
// errgroup will return context canceled for the quicConn.Serve
connLogger.Logger().Debug().Msg("Forcefully breaking quic connection")
quicConn.Close()
}
return err
})
+38
View File
@@ -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
+7
View File
@@ -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
+28
View File
@@ -0,0 +1,28 @@
#!/bin/bash
cd "$(dirname $0)"
go get golang.org/x/lint/golint
DIRS=". tcpassembly tcpassembly/tcpreader ip4defrag reassembly macs pcapgo pcap afpacket pfring routing defrag/lcmdefrag"
# Add subdirectories here as we clean up golint on each.
for subdir in $DIRS; do
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 *.go; do
if cat .lint_blacklist | grep -q $file; then
echo "Skipping lint of $file due to .lint_blacklist"
elif golint $file | grep .; then
echo "Lint error in file $file"
exit 1
fi
done
popd
+10
View File
@@ -0,0 +1,10 @@
#!/bin/bash
cd "$(dirname $0)"
DIRS=". layers pcap pcapgo tcpassembly tcpassembly/tcpreader routing ip4defrag bytediff macs defrag/lcmdefrag"
set -e
for subdir in $DIRS; do
pushd $subdir
go vet
popd
done
+9
View File
@@ -0,0 +1,9 @@
#!/bin/bash
set -ev
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
go get github.com/google/gopacket/pcapgo
+10
View File
@@ -0,0 +1,10 @@
#!/bin/bash
set -ev
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
go test github.com/google/gopacket/pcapgo
go test github.com/google/gopacket/pcap
+57
View File
@@ -0,0 +1,57 @@
language: go
go:
- 1.11.x
- 1.12.x
- 1.13.x
- master
addons:
apt:
packages:
libpcap-dev
# use modules except for older versions (see below)
install: true
env:
- GO111MODULE=on
script: ./.travis.script.sh
matrix:
fast_finish: true
allow_failures:
- go: master
jobs:
include:
- go: 1.5.x
install: ./.travis.install.sh
- go: 1.6.x
install: ./.travis.install.sh
- go: 1.7.x
install: ./.travis.install.sh
- go: 1.8.x
install: ./.travis.install.sh
- go: 1.9.x
install: ./.travis.install.sh
- go: 1.10.x
install: ./.travis.install.sh
- os: osx
go: 1.x
# windows doesn't work on travis (package installation just hangs and then errors out)
# - os: windows
# go: 1.x
# # We don't need nmap - but that's the only way to get npcap:
# before_install: choco install npcap --version 0.86 -y
- stage: style
name: "fmt/vet/lint"
go: 1.x
script:
- ./.travis.gofmt.sh
- ./.travis.govet.sh
- ./.travis.golint.sh
stages:
- style
- test
+54
View File
@@ -0,0 +1,54 @@
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>
Christian Mäder <christian.maeder@nine.ch>
Gernot Vormayr <gvormayr@gmail.com>
Vitor Garcia Graveto <victor.graveto@gmail.com>
Elias Chavarria Reyes <elchavar@cisco.com>
Daniel Rittweiler <ripx80@protonmail.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>
Jose Selvi <jselvi@pentester.es>
Yerden Zhumabekov <yerden.zhumabekov@gmail.com>
Jensen Hwa <jensenhwa@gmail.com>
-----------------------------------------------
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>
+215
View File
@@ -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.
+28
View File
@@ -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.
+12
View File
@@ -0,0 +1,12 @@
# GoPacket
This library provides packet decoding capabilities for Go.
See [godoc](https://godoc.org/github.com/google/gopacket) for more details.
[![Build Status](https://travis-ci.org/google/gopacket.svg?branch=master)](https://travis-ci.org/google/gopacket)
[![GoDoc](https://godoc.org/github.com/google/gopacket?status.svg)](https://godoc.org/github.com/google/gopacket)
Minimum Go version required is 1.5 except for pcapgo/EthernetHandle, afpacket, and bsdbpf which need at least 1.9 due to x/sys/unix dependencies.
Originally forked from the gopcap project written by Andreas
Krennmair <ak@synflood.at> (http://github.com/akrennmair/gopcap).
+178
View File
@@ -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 err
}
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 err
}
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
}
+157
View File
@@ -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")
}
+432
View File
@@ -0,0 +1,432 @@
// 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.
Minimum go version required is 1.5 except for pcapgo/EthernetHandle, afpacket,
and bsdbpf which need at least 1.7 due to x/sys/unix dependencies.
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.FlowFromEndpoints(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, gopacket.LayerTypeMetadata{Name: "MyLayerType", Decoder: gopacket.DecodeFunc(decodeMyLayer)})
// Implement my layer
type MyLayer struct {
StrangeHeader []byte
payload []byte
}
func (m MyLayer) LayerType() gopacket.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, &eth, &ip4, &ip6, &tcp)
decoded := []gopacket.LayerType{}
for packetData := range somehowGetPacketData() {
if err := parser.DecodeLayers(packetData, &decoded); err != nil {
fmt.Fprintf(os.Stderr, "Could not decode layers: %v\n", err)
continue
}
for _, layerType := range decoded {
switch layerType {
case layers.LayerTypeIPv6:
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.
Faster And Customized Decoding with DecodingLayerContainer
By default, DecodingLayerParser uses native map to store and search for a layer
to decode. Though being versatile, in some cases this solution may be not so
optimal. For example, if you have only few layers faster operations may be
provided by sparse array indexing or linear array scan.
To accomodate these scenarios, DecodingLayerContainer interface is introduced
along with its implementations: DecodingLayerSparse, DecodingLayerArray and
DecodingLayerMap. You can specify a container implementation to
DecodingLayerParser with SetDecodingLayerContainer method. Example:
dlp := gopacket.NewDecodingLayerParser(LayerTypeEthernet)
dlp.SetDecodingLayerContainer(gopacket.DecodingLayerSparse(nil))
var eth layers.Ethernet
dlp.AddDecodingLayer(&eth)
// ... add layers and use DecodingLayerParser as usual...
To skip one level of indirection (though sacrificing some capabilities) you may
also use DecodingLayerContainer as a decoding tool as it is. In this case you have to
handle unknown layer types and layer panics by yourself. Example:
func main() {
var eth layers.Ethernet
var ip4 layers.IPv4
var ip6 layers.IPv6
var tcp layers.TCP
dlc := gopacket.DecodingLayerContainer(gopacket.DecodingLayerArray(nil))
dlc = dlc.Put(&eth)
dlc = dlc.Put(&ip4)
dlc = dlc.Put(&ip6)
dlc = dlc.Put(&tcp)
// you may specify some meaningful DecodeFeedback
decoder := dlc.LayersDecoder(LayerTypeEthernet, gopacket.NilDecodeFeedback)
decoded := make([]gopacket.LayerType, 0, 20)
for packetData := range somehowGetPacketData() {
lt, err := decoder(packetData, &decoded)
if err != nil {
fmt.Fprintf(os.Stderr, "Could not decode layers: %v\n", err)
continue
}
if lt != gopacket.LayerTypeZero {
fmt.Fprintf(os.Stderr, "unknown layer type: %v\n", lt)
continue
}
for _, layerType := range decoded {
// examine decoded layertypes just as already shown above
}
}
}
DecodingLayerSparse is the fastest but most effective when LayerType values
that layers in use can decode are not large because otherwise that would lead
to bigger memory footprint. DecodingLayerArray is very compact and primarily
usable if the number of decoding layers is not big (up to ~10-15, but please do
your own benchmarks). DecodingLayerMap is the most versatile one and used by
DecodingLayerParser by default. Please refer to tests and benchmarks in layers
subpackage to further examine usage examples and performance measurements.
You may also choose to implement your own DecodingLayerContainer if you want to
make use of your own internal packet decoding logic.
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
+236
View File
@@ -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 lexicographically.
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)
+288
View File
@@ -0,0 +1,288 @@
#!/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
go run gen2.go | gofmt > enums_generated.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/afpacket
go build
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
pushd ip4defrag
go test ./...
popd
pushd defrag
go test ./...
popd
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
+107
View 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)
}
+39
View File
@@ -0,0 +1,39 @@
dot11.go
eap.go
endpoints.go
enums_generated.go
enums.go
ethernet.go
geneve.go
icmp4.go
icmp6.go
igmp.go
ip4.go
ip6.go
layertypes.go
linux_sll.go
llc.go
lldp.go
mpls.go
ndp.go
ntp.go
ospf.go
pflog.go
pppoe.go
prism.go
radiotap.go
rudp.go
sctp.go
sflow.go
tcp.go
tcpip.go
tls.go
tls_alert.go
tls_appdata.go
tls_cipherspec.go
tls_hanshake.go
tls_test.go
udp.go
udplite.go
usb.go
vrrp.go
+118
View File
@@ -0,0 +1,118 @@
// 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"
)
// 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 {
if len(data) < 8 {
df.SetTruncated()
return fmt.Errorf("ARP length %d too short", len(data))
}
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])
arpLength := 8 + 2*arp.HwAddressSize + 2*arp.ProtAddressSize
if len(data) < int(arpLength) {
df.SetTruncated()
return fmt.Errorf("ARP length %d too short, %d expected", len(data), arpLength)
}
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]
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)
}
+166
View File
@@ -0,0 +1,166 @@
// Copyright 2019 The GoPacket Authors. All rights reserved.
//
// Use of this source code is governed by a BSD-style license that can be found
// in the LICENSE file in the root of the source tree.
package layers
// This file implements the ASF RMCP payload specified in section 3.2.2.3 of
// https://www.dmtf.org/sites/default/files/standards/documents/DSP0136.pdf
import (
"encoding/binary"
"fmt"
"github.com/google/gopacket"
)
const (
// ASFRMCPEnterprise is the IANA-assigned Enterprise Number of the ASF-RMCP.
ASFRMCPEnterprise uint32 = 4542
)
// ASFDataIdentifier encapsulates fields used to uniquely identify the format of
// the data block.
//
// While the enterprise number is almost always 4542 (ASF-RMCP), we support
// registering layers using structs of this type as a key in case any users are
// using OEM-extensions.
type ASFDataIdentifier struct {
// Enterprise is the IANA Enterprise Number associated with the entity that
// defines the message type. A list can be found at
// https://www.iana.org/assignments/enterprise-numbers/enterprise-numbers.
// This can be thought of as the namespace for the message type.
Enterprise uint32
// Type is the message type, defined by the entity associated with the
// enterprise above. No pressure, but in the context of EN 4542, 1 byte is
// the difference between sending a ping and telling a machine to do an
// unconditional power down (0x80 and 0x12 respectively).
Type uint8
}
// LayerType returns the payload layer type corresponding to an ASF message
// type.
func (a ASFDataIdentifier) LayerType() gopacket.LayerType {
if lt := asfDataLayerTypes[a]; lt != 0 {
return lt
}
// some layer types don't have a payload, e.g. ASF-RMCP Presence Ping.
return gopacket.LayerTypePayload
}
// RegisterASFLayerType allows specifying that the data block of ASF packets
// with a given enterprise number and type should be processed by a given layer
// type. This overrides any existing registrations, including defaults.
func RegisterASFLayerType(a ASFDataIdentifier, l gopacket.LayerType) {
asfDataLayerTypes[a] = l
}
var (
// ASFDataIdentifierPresencePong is the message type of the response to a
// Presence Ping message. It indicates the sender is ASF-RMCP-aware.
ASFDataIdentifierPresencePong = ASFDataIdentifier{
Enterprise: ASFRMCPEnterprise,
Type: 0x40,
}
// ASFDataIdentifierPresencePing is a message type sent to a managed client
// to solicit a Presence Pong response. Clients may ignore this if the RMCP
// version is unsupported. Sending this message with a sequence number <255
// is the recommended way of finding out whether an implementation sends
// RMCP ACKs (e.g. iDRAC does, Super Micro does not).
//
// Systems implementing IPMI must respond to this ping to conform to the
// spec, so it is a good substitute for an ICMP ping.
ASFDataIdentifierPresencePing = ASFDataIdentifier{
Enterprise: ASFRMCPEnterprise,
Type: 0x80,
}
// asfDataLayerTypes is used to find the next layer for a given ASF header.
asfDataLayerTypes = map[ASFDataIdentifier]gopacket.LayerType{
ASFDataIdentifierPresencePong: LayerTypeASFPresencePong,
}
)
// ASF defines ASF's generic RMCP message Data block format. See section
// 3.2.2.3.
type ASF struct {
BaseLayer
ASFDataIdentifier
// Tag is used to match request/response pairs. The tag of a response is set
// to that of the message it is responding to. If a message is
// unidirectional, i.e. not part of a request/response pair, this is set to
// 255.
Tag uint8
// 1 byte reserved, set to 0x00.
// Length is the length of this layer's payload in bytes.
Length uint8
}
// LayerType returns LayerTypeASF. It partially satisfies Layer and
// SerializableLayer.
func (*ASF) LayerType() gopacket.LayerType {
return LayerTypeASF
}
// CanDecode returns LayerTypeASF. It partially satisfies DecodingLayer.
func (a *ASF) CanDecode() gopacket.LayerClass {
return a.LayerType()
}
// DecodeFromBytes makes the layer represent the provided bytes. It partially
// satisfies DecodingLayer.
func (a *ASF) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
if len(data) < 8 {
df.SetTruncated()
return fmt.Errorf("invalid ASF data header, length %v less than 8",
len(data))
}
a.BaseLayer.Contents = data[:8]
a.BaseLayer.Payload = data[8:]
a.Enterprise = binary.BigEndian.Uint32(data[:4])
a.Type = uint8(data[4])
a.Tag = uint8(data[5])
// 1 byte reserved
a.Length = uint8(data[7])
return nil
}
// NextLayerType returns the layer type corresponding to the message type of
// this ASF data layer. This partially satisfies DecodingLayer.
func (a *ASF) NextLayerType() gopacket.LayerType {
return a.ASFDataIdentifier.LayerType()
}
// SerializeTo writes the serialized fom of this layer into the SerializeBuffer,
// partially satisfying SerializableLayer.
func (a *ASF) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
payload := b.Bytes()
bytes, err := b.PrependBytes(8)
if err != nil {
return err
}
binary.BigEndian.PutUint32(bytes[:4], a.Enterprise)
bytes[4] = uint8(a.Type)
bytes[5] = a.Tag
bytes[6] = 0x00
if opts.FixLengths {
a.Length = uint8(len(payload))
}
bytes[7] = a.Length
return nil
}
// decodeASF decodes the byte slice into an RMCP-ASF data struct.
func decodeASF(data []byte, p gopacket.PacketBuilder) error {
return decodingLayerDecoder(&ASF{}, data, p)
}
+194
View File
@@ -0,0 +1,194 @@
// Copyright 2019 The GoPacket Authors. All rights reserved.
//
// Use of this source code is governed by a BSD-style license that can be found
// in the LICENSE file in the root of the source tree.
package layers
// This file implements the RMCP ASF Presence Pong message, specified in section
// 3.2.4.3 of
// https://www.dmtf.org/sites/default/files/standards/documents/DSP0136.pdf. It
// also contains non-competing elements from IPMI v2.0, specified in section
// 13.2.4 of
// https://www.intel.com/content/dam/www/public/us/en/documents/specification-updates/ipmi-intelligent-platform-mgt-interface-spec-2nd-gen-v2-0-spec-update.pdf.
import (
"encoding/binary"
"fmt"
"github.com/google/gopacket"
)
type (
// ASFEntity is the type of individual entities that a Presence Pong
// response can indicate support of. The entities currently implemented by
// the spec are IPMI and ASFv1.
ASFEntity uint8
// ASFInteraction is the type of individual interactions that a Presence
// Pong response can indicate support for. The interactions currently
// implemented by the spec are RMCP security extensions. Although not
// specified, IPMI uses this field to indicate support for DASH, which is
// supported as well.
ASFInteraction uint8
)
const (
// ASFDCMIEnterprise is the IANA-assigned Enterprise Number of the Data
// Center Manageability Interface Forum. The Presence Pong response's
// Enterprise field being set to this value indicates support for DCMI. The
// DCMI spec regards the OEM field as reserved, so these should be null.
ASFDCMIEnterprise uint32 = 36465
// ASFPresencePongEntityIPMI ANDs with Presence Pong's supported entities
// field if the managed system supports IPMI.
ASFPresencePongEntityIPMI ASFEntity = 1 << 7
// ASFPresencePongEntityASFv1 ANDs with Presence Pong's supported entities
// field if the managed system supports ASF v1.0.
ASFPresencePongEntityASFv1 ASFEntity = 1
// ASFPresencePongInteractionSecurityExtensions ANDs with Presence Pong's
// supported interactions field if the managed system supports RMCP v2.0
// security extensions. See section 3.2.3.
ASFPresencePongInteractionSecurityExtensions ASFInteraction = 1 << 7
// ASFPresencePongInteractionDASH ANDs with Presence Pong's supported
// interactions field if the managed system supports DMTF DASH. See
// https://www.dmtf.org/standards/dash.
ASFPresencePongInteractionDASH ASFInteraction = 1 << 5
)
// ASFPresencePong defines the structure of a Presence Pong message's payload.
// See section 3.2.4.3.
type ASFPresencePong struct {
BaseLayer
// Enterprise is the IANA Enterprise Number of an entity that has defined
// OEM-specific capabilities for the managed client. If no such capabilities
// exist, this is set to ASF's IANA Enterprise Number.
Enterprise uint32
// OEM identifies OEM-specific capabilities. Its structure is defined by the
// OEM. This is set to 0s if no OEM-specific capabilities exist. This
// implementation does not change byte order from the wire for this field.
OEM [4]byte
// We break out entities and interactions into separate booleans as
// discovery is the entire point of this type of message, so we assume they
// are accessed. It also makes gopacket's default layer printing more
// useful.
// IPMI is true if IPMI is supported by the managed system. There is no
// explicit version in the specification, however given the dates, this is
// assumed to be IPMI v1.0. Support for IPMI is contained in the "supported
// entities" field of the presence pong payload.
IPMI bool
// ASFv1 indicates support for ASF v1.0. This seems somewhat redundant as
// ASF must be supported in order to receive a response. This is contained
// in the "supported entities" field of the presence pong payload.
ASFv1 bool
// SecurityExtensions indicates support for RMCP Security Extensions,
// specified in ASF v2.0. This will always be false for v1.x
// implementations. This is contained in the "supported interactions" field
// of the presence pong payload. This field is defined in ASF v1.0, but has
// no useful value.
SecurityExtensions bool
// DASH is true if DMTF DASH is supported. This is not specified in ASF
// v2.0, but in IPMI v2.0, however the former does not preclude it, so we
// support it.
DASH bool
// 6 bytes reserved after the entities and interactions fields, set to 0s.
}
// SupportsDCMI returns whether the Presence Pong message indicates support for
// the Data Center Management Interface, which is an extension of IPMI v2.0.
func (a *ASFPresencePong) SupportsDCMI() bool {
return a.Enterprise == ASFDCMIEnterprise && a.IPMI && a.ASFv1
}
// LayerType returns LayerTypeASFPresencePong. It partially satisfies Layer and
// SerializableLayer.
func (*ASFPresencePong) LayerType() gopacket.LayerType {
return LayerTypeASFPresencePong
}
// CanDecode returns LayerTypeASFPresencePong. It partially satisfies
// DecodingLayer.
func (a *ASFPresencePong) CanDecode() gopacket.LayerClass {
return a.LayerType()
}
// DecodeFromBytes makes the layer represent the provided bytes. It partially
// satisfies DecodingLayer.
func (a *ASFPresencePong) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
if len(data) < 16 {
df.SetTruncated()
return fmt.Errorf("invalid ASF presence pong payload, length %v less than 16",
len(data))
}
a.BaseLayer.Contents = data[:16]
a.BaseLayer.Payload = data[16:]
a.Enterprise = binary.BigEndian.Uint32(data[:4])
copy(a.OEM[:], data[4:8]) // N.B. no byte order change
a.IPMI = data[8]&uint8(ASFPresencePongEntityIPMI) != 0
a.ASFv1 = data[8]&uint8(ASFPresencePongEntityASFv1) != 0
a.SecurityExtensions = data[9]&uint8(ASFPresencePongInteractionSecurityExtensions) != 0
a.DASH = data[9]&uint8(ASFPresencePongInteractionDASH) != 0
// ignore remaining 6 bytes; should be set to 0s
return nil
}
// NextLayerType returns LayerTypePayload, as there are no further layers to
// decode. This partially satisfies DecodingLayer.
func (a *ASFPresencePong) NextLayerType() gopacket.LayerType {
return gopacket.LayerTypePayload
}
// SerializeTo writes the serialized fom of this layer into the SerializeBuffer,
// partially satisfying SerializableLayer.
func (a *ASFPresencePong) SerializeTo(b gopacket.SerializeBuffer, _ gopacket.SerializeOptions) error {
bytes, err := b.PrependBytes(16)
if err != nil {
return err
}
binary.BigEndian.PutUint32(bytes[:4], a.Enterprise)
copy(bytes[4:8], a.OEM[:])
bytes[8] = 0
if a.IPMI {
bytes[8] |= uint8(ASFPresencePongEntityIPMI)
}
if a.ASFv1 {
bytes[8] |= uint8(ASFPresencePongEntityASFv1)
}
bytes[9] = 0
if a.SecurityExtensions {
bytes[9] |= uint8(ASFPresencePongInteractionSecurityExtensions)
}
if a.DASH {
bytes[9] |= uint8(ASFPresencePongInteractionDASH)
}
// zero-out remaining 6 bytes
for i := 10; i < len(bytes); i++ {
bytes[i] = 0x00
}
return nil
}
// decodeASFPresencePong decodes the byte slice into an RMCP-ASF Presence Pong
// struct.
func decodeASFPresencePong(data []byte, p gopacket.PacketBuilder) error {
return decodingLayerDecoder(&ASFPresencePong{}, data, p)
}
+52
View File
@@ -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
+481
View File
@@ -0,0 +1,481 @@
// Copyright 2017 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"
"github.com/google/gopacket"
)
// BFD Control Packet Format
// -------------------------
// The current version of BFD's RFC (RFC 5880) contains the following
// diagram for the BFD Control packet format:
//
// 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
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// |Vers | Diag |Sta|P|F|C|A|D|M| Detect Mult | Length |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | My Discriminator |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | Your Discriminator |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | Desired Min TX Interval |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | Required Min RX Interval |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | Required Min Echo RX Interval |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
//
// An optional Authentication Section MAY be present:
//
// 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
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | Auth Type | Auth Len | Authentication Data... |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
//
//
// Simple Password Authentication Section Format
// ---------------------------------------------
// 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
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | Auth Type | Auth Len | Auth Key ID | Password... |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | ... |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
//
//
// Keyed MD5 and Meticulous Keyed MD5 Authentication Section Format
// ----------------------------------------------------------------
// 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
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | Auth Type | Auth Len | Auth Key ID | Reserved |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | Sequence Number |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | Auth Key/Digest... |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | ... |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
//
//
// Keyed SHA1 and Meticulous Keyed SHA1 Authentication Section Format
// ------------------------------------------------------------------
// 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
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | Auth Type | Auth Len | Auth Key ID | Reserved |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | Sequence Number |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | Auth Key/Hash... |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | ... |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
//
// From https://tools.ietf.org/rfc/rfc5880.txt
const bfdMinimumRecordSizeInBytes int = 24
// BFDVersion represents the version as decoded from the BFD control message
type BFDVersion uint8
// BFDDiagnostic represents diagnostic infomation about a BFD session
type BFDDiagnostic uint8
// constants that define BFDDiagnostic flags
const (
BFDDiagnosticNone BFDDiagnostic = 0 // No Diagnostic
BFDDiagnosticTimeExpired BFDDiagnostic = 1 // Control Detection Time Expired
BFDDiagnosticEchoFailed BFDDiagnostic = 2 // Echo Function Failed
BFDDiagnosticNeighborSignalDown BFDDiagnostic = 3 // Neighbor Signaled Session Down
BFDDiagnosticForwardPlaneReset BFDDiagnostic = 4 // Forwarding Plane Reset
BFDDiagnosticPathDown BFDDiagnostic = 5 // Path Down
BFDDiagnosticConcatPathDown BFDDiagnostic = 6 // Concatenated Path Down
BFDDiagnosticAdminDown BFDDiagnostic = 7 // Administratively Down
BFDDiagnosticRevConcatPathDown BFDDiagnostic = 8 // Reverse Concatenated Path Dow
)
// String returns a string version of BFDDiagnostic
func (bd BFDDiagnostic) String() string {
switch bd {
default:
return "Unknown"
case BFDDiagnosticNone:
return "None"
case BFDDiagnosticTimeExpired:
return "Control Detection Time Expired"
case BFDDiagnosticEchoFailed:
return "Echo Function Failed"
case BFDDiagnosticNeighborSignalDown:
return "Neighbor Signaled Session Down"
case BFDDiagnosticForwardPlaneReset:
return "Forwarding Plane Reset"
case BFDDiagnosticPathDown:
return "Path Down"
case BFDDiagnosticConcatPathDown:
return "Concatenated Path Down"
case BFDDiagnosticAdminDown:
return "Administratively Down"
case BFDDiagnosticRevConcatPathDown:
return "Reverse Concatenated Path Down"
}
}
// BFDState represents the state of a BFD session
type BFDState uint8
// constants that define BFDState
const (
BFDStateAdminDown BFDState = 0
BFDStateDown BFDState = 1
BFDStateInit BFDState = 2
BFDStateUp BFDState = 3
)
// String returns a string version of BFDState
func (s BFDState) String() string {
switch s {
default:
return "Unknown"
case BFDStateAdminDown:
return "Admin Down"
case BFDStateDown:
return "Down"
case BFDStateInit:
return "Init"
case BFDStateUp:
return "Up"
}
}
// BFDDetectMultiplier represents the negotiated transmit interval,
// multiplied by this value, provides the Detection Time for the
// receiving system in Asynchronous mode.
type BFDDetectMultiplier uint8
// BFDDiscriminator is a unique, nonzero discriminator value used
// to demultiplex multiple BFD sessions between the same pair of systems.
type BFDDiscriminator uint32
// BFDTimeInterval represents a time interval in microseconds
type BFDTimeInterval uint32
// BFDAuthType represents the authentication used in the BFD session
type BFDAuthType uint8
// constants that define the BFDAuthType
const (
BFDAuthTypeNone BFDAuthType = 0 // No Auth
BFDAuthTypePassword BFDAuthType = 1 // Simple Password
BFDAuthTypeKeyedMD5 BFDAuthType = 2 // Keyed MD5
BFDAuthTypeMeticulousKeyedMD5 BFDAuthType = 3 // Meticulous Keyed MD5
BFDAuthTypeKeyedSHA1 BFDAuthType = 4 // Keyed SHA1
BFDAuthTypeMeticulousKeyedSHA1 BFDAuthType = 5 // Meticulous Keyed SHA1
)
// String returns a string version of BFDAuthType
func (at BFDAuthType) String() string {
switch at {
default:
return "Unknown"
case BFDAuthTypeNone:
return "No Authentication"
case BFDAuthTypePassword:
return "Simple Password"
case BFDAuthTypeKeyedMD5:
return "Keyed MD5"
case BFDAuthTypeMeticulousKeyedMD5:
return "Meticulous Keyed MD5"
case BFDAuthTypeKeyedSHA1:
return "Keyed SHA1"
case BFDAuthTypeMeticulousKeyedSHA1:
return "Meticulous Keyed SHA1"
}
}
// BFDAuthKeyID represents the authentication key ID in use for
// this packet. This allows multiple keys to be active simultaneously.
type BFDAuthKeyID uint8
// BFDAuthSequenceNumber represents the sequence number for this packet.
// For Keyed Authentication, this value is incremented occasionally. For
// Meticulous Keyed Authentication, this value is incremented for each
// successive packet transmitted for a session. This provides protection
// against replay attacks.
type BFDAuthSequenceNumber uint32
// BFDAuthData represents the authentication key or digest
type BFDAuthData []byte
// BFDAuthHeader represents authentication data used in the BFD session
type BFDAuthHeader struct {
AuthType BFDAuthType
KeyID BFDAuthKeyID
SequenceNumber BFDAuthSequenceNumber
Data BFDAuthData
}
// Length returns the data length of the BFDAuthHeader based on the
// authentication type
func (h *BFDAuthHeader) Length() int {
switch h.AuthType {
case BFDAuthTypePassword:
return 3 + len(h.Data)
case BFDAuthTypeKeyedMD5, BFDAuthTypeMeticulousKeyedMD5:
return 8 + len(h.Data)
case BFDAuthTypeKeyedSHA1, BFDAuthTypeMeticulousKeyedSHA1:
return 8 + len(h.Data)
default:
return 0
}
}
// BFD represents a BFD control message packet whose payload contains
// the control information required to for a BFD session.
//
// References
// ----------
//
// Wikipedia's BFD entry:
// https://en.wikipedia.org/wiki/Bidirectional_Forwarding_Detection
// This is the best place to get an overview of BFD.
//
// RFC 5880 "Bidirectional Forwarding Detection (BFD)" (2010)
// https://tools.ietf.org/html/rfc5880
// This is the original BFD specification.
//
// RFC 5881 "Bidirectional Forwarding Detection (BFD) for IPv4 and IPv6 (Single Hop)" (2010)
// https://tools.ietf.org/html/rfc5881
// Describes the use of the Bidirectional Forwarding Detection (BFD)
// protocol over IPv4 and IPv6 for single IP hops.
type BFD struct {
BaseLayer // Stores the packet bytes and payload bytes.
Version BFDVersion // Version of the BFD protocol.
Diagnostic BFDDiagnostic // Diagnostic code for last state change
State BFDState // Current state
Poll bool // Requesting verification
Final bool // Responding to a received BFD Control packet that had the Poll (P) bit set.
ControlPlaneIndependent bool // BFD implementation does not share fate with its control plane
AuthPresent bool // Authentication Section is present and the session is to be authenticated
Demand bool // Demand mode is active
Multipoint bool // For future point-to-multipoint extensions. Must always be zero
DetectMultiplier BFDDetectMultiplier // Detection time multiplier
MyDiscriminator BFDDiscriminator // A unique, nonzero discriminator value
YourDiscriminator BFDDiscriminator // discriminator received from the remote system.
DesiredMinTxInterval BFDTimeInterval // Minimum interval, in microseconds, the local system would like to use when transmitting BFD Control packets
RequiredMinRxInterval BFDTimeInterval // Minimum interval, in microseconds, between received BFD Control packets that this system is capable of supporting
RequiredMinEchoRxInterval BFDTimeInterval // Minimum interval, in microseconds, between received BFD Echo packets that this system is capable of supporting
AuthHeader *BFDAuthHeader // Authentication data, variable length.
}
// Length returns the data length of a BFD Control message which
// changes based on the presence and type of authentication
// contained in the message
func (d *BFD) Length() int {
if d.AuthPresent && (d.AuthHeader != nil) {
return bfdMinimumRecordSizeInBytes + d.AuthHeader.Length()
}
return bfdMinimumRecordSizeInBytes
}
// LayerType returns the layer type of the BFD object, which is LayerTypeBFD.
func (d *BFD) LayerType() gopacket.LayerType {
return LayerTypeBFD
}
// decodeBFD analyses a byte slice and attempts to decode it as a BFD
// control packet
//
// If it succeeds, it loads p with information about the packet and returns nil.
// If it fails, it returns an error (non nil).
//
// This function is employed in layertypes.go to register the BFD layer.
func decodeBFD(data []byte, p gopacket.PacketBuilder) error {
// Attempt to decode the byte slice.
d := &BFD{}
err := d.DecodeFromBytes(data, p)
if err != nil {
return err
}
// If the decoding worked, add the layer to the packet and set it
// as the application layer too, if there isn't already one.
p.AddLayer(d)
p.SetApplicationLayer(d)
return nil
}
// DecodeFromBytes analyses a byte slice and attempts to decode it as a BFD
// control packet.
//
// Upon succeeds, it loads the BFD object with information about the packet
// and returns nil.
// Upon failure, it returns an error (non nil).
func (d *BFD) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
// If the data block is too short to be a BFD record, then return an error.
if len(data) < bfdMinimumRecordSizeInBytes {
df.SetTruncated()
return errors.New("BFD packet too short")
}
pLen := uint8(data[3])
if len(data) != int(pLen) {
return errors.New("BFD packet length does not match")
}
// BFD type embeds type BaseLayer which contains two fields:
// Contents is supposed to contain the bytes of the data at this level.
// Payload is supposed to contain the payload of this level.
// Here we set the baselayer to be the bytes of the BFD record.
d.BaseLayer = BaseLayer{Contents: data[:len(data)]}
// Extract the fields from the block of bytes.
// To make sense of this, refer to the packet diagram
// above and the section on endian conventions.
// The first few fields are all packed into the first 32 bits. Unpack them.
d.Version = BFDVersion(((data[0] & 0xE0) >> 5))
d.Diagnostic = BFDDiagnostic(data[0] & 0x1F)
data = data[1:]
d.State = BFDState((data[0] & 0xC0) >> 6)
d.Poll = data[0]&0x20 != 0
d.Final = data[0]&0x10 != 0
d.ControlPlaneIndependent = data[0]&0x08 != 0
d.AuthPresent = data[0]&0x04 != 0
d.Demand = data[0]&0x02 != 0
d.Multipoint = data[0]&0x01 != 0
data = data[1:]
data, d.DetectMultiplier = data[1:], BFDDetectMultiplier(data[0])
data, _ = data[1:], uint8(data[0]) // Consume length
// The remaining fields can just be copied in big endian order.
data, d.MyDiscriminator = data[4:], BFDDiscriminator(binary.BigEndian.Uint32(data[:4]))
data, d.YourDiscriminator = data[4:], BFDDiscriminator(binary.BigEndian.Uint32(data[:4]))
data, d.DesiredMinTxInterval = data[4:], BFDTimeInterval(binary.BigEndian.Uint32(data[:4]))
data, d.RequiredMinRxInterval = data[4:], BFDTimeInterval(binary.BigEndian.Uint32(data[:4]))
data, d.RequiredMinEchoRxInterval = data[4:], BFDTimeInterval(binary.BigEndian.Uint32(data[:4]))
if d.AuthPresent && (len(data) > 2) {
d.AuthHeader = &BFDAuthHeader{}
data, d.AuthHeader.AuthType = data[1:], BFDAuthType(data[0])
data, _ = data[1:], uint8(data[0]) // Consume length
data, d.AuthHeader.KeyID = data[1:], BFDAuthKeyID(data[0])
switch d.AuthHeader.AuthType {
case BFDAuthTypePassword:
d.AuthHeader.Data = BFDAuthData(data)
case BFDAuthTypeKeyedMD5, BFDAuthTypeMeticulousKeyedMD5:
// Skipped reserved byte
data, d.AuthHeader.SequenceNumber = data[5:], BFDAuthSequenceNumber(binary.BigEndian.Uint32(data[1:5]))
d.AuthHeader.Data = BFDAuthData(data)
case BFDAuthTypeKeyedSHA1, BFDAuthTypeMeticulousKeyedSHA1:
// Skipped reserved byte
data, d.AuthHeader.SequenceNumber = data[5:], BFDAuthSequenceNumber(binary.BigEndian.Uint32(data[1:5]))
d.AuthHeader.Data = BFDAuthData(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 (d *BFD) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
data, err := b.PrependBytes(bfdMinimumRecordSizeInBytes)
if err != nil {
return err
}
// Pack the first few fields into the first 32 bits.
data[0] = byte(byte(d.Version<<5) | byte(d.Diagnostic))
h := uint8(0)
h |= (uint8(d.State) << 6)
h |= (uint8(bool2uint8(d.Poll)) << 5)
h |= (uint8(bool2uint8(d.Final)) << 4)
h |= (uint8(bool2uint8(d.ControlPlaneIndependent)) << 3)
h |= (uint8(bool2uint8(d.AuthPresent)) << 2)
h |= (uint8(bool2uint8(d.Demand)) << 1)
h |= uint8(bool2uint8(d.Multipoint))
data[1] = byte(h)
data[2] = byte(d.DetectMultiplier)
data[3] = byte(d.Length())
// The remaining fields can just be copied in big endian order.
binary.BigEndian.PutUint32(data[4:], uint32(d.MyDiscriminator))
binary.BigEndian.PutUint32(data[8:], uint32(d.YourDiscriminator))
binary.BigEndian.PutUint32(data[12:], uint32(d.DesiredMinTxInterval))
binary.BigEndian.PutUint32(data[16:], uint32(d.RequiredMinRxInterval))
binary.BigEndian.PutUint32(data[20:], uint32(d.RequiredMinEchoRxInterval))
if d.AuthPresent && (d.AuthHeader != nil) {
auth, err := b.AppendBytes(int(d.AuthHeader.Length()))
if err != nil {
return err
}
auth[0] = byte(d.AuthHeader.AuthType)
auth[1] = byte(d.AuthHeader.Length())
auth[2] = byte(d.AuthHeader.KeyID)
switch d.AuthHeader.AuthType {
case BFDAuthTypePassword:
copy(auth[3:], d.AuthHeader.Data)
case BFDAuthTypeKeyedMD5, BFDAuthTypeMeticulousKeyedMD5:
auth[3] = byte(0)
binary.BigEndian.PutUint32(auth[4:], uint32(d.AuthHeader.SequenceNumber))
copy(auth[8:], d.AuthHeader.Data)
case BFDAuthTypeKeyedSHA1, BFDAuthTypeMeticulousKeyedSHA1:
auth[3] = byte(0)
binary.BigEndian.PutUint32(auth[4:], uint32(d.AuthHeader.SequenceNumber))
copy(auth[8:], d.AuthHeader.Data)
}
}
return nil
}
// CanDecode returns a set of layers that BFD objects can decode.
// As BFD objects can only decide the BFD layer, we can return just that layer.
// Apparently a single layer type implements LayerClass.
func (d *BFD) CanDecode() gopacket.LayerClass {
return LayerTypeBFD
}
// NextLayerType specifies the next layer that GoPacket should attempt to
// analyse after this (BFD) layer. As BFD packets do not contain any payload
// bytes, there are no further layers to analyse.
func (d *BFD) NextLayerType() gopacket.LayerType {
return gopacket.LayerTypeZero
}
// Payload returns an empty byte slice as BFD packets do not carry a payload
func (d *BFD) Payload() []byte {
return nil
}
// bool2uint8 converts a bool to uint8
func bool2uint8(b bool) uint8 {
if b {
return 1
}
return 0
}
+659
View File
@@ -0,0 +1,659 @@
// 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"
"errors"
"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:], p)
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, p gopacket.PacketBuilder) (values []CiscoDiscoveryValue, err error) {
for len(data) > 0 {
if len(data) < 4 {
p.SetTruncated()
return nil, errors.New("CDP TLV < 4 bytes")
}
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
} else if len(data) < int(val.Length) {
p.SetTruncated()
return nil, fmt.Errorf("CDP TLV < length %d", val.Length)
}
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, p)
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
}
+109
View File
@@ -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)
}
+592
View File
@@ -0,0 +1,592 @@
// 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"
"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 {
if len(data) < 240 {
df.SetTruncated()
return fmt.Errorf("DHCPv4 length %d too short", len(data))
}
d.Options = d.Options[:0]
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 InvalidMagicCookie
}
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
}
}
d.Contents = data
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:
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 DecOptionNotEnoughData
}
o.Type = DHCPOpt(data[0])
switch o.Type {
case DHCPOptPad, DHCPOptEnd:
o.Data = nil
default:
if len(data) < 2 {
return DecOptionNotEnoughData
}
o.Length = data[1]
if int(o.Length) > len(data[2:]) {
return DecOptionMalformed
}
o.Data = data[2 : 2+int(o.Length)]
}
return nil
}
// DHCPv4Error is used for constant errors for DHCPv4. It is needed for test asserts.
type DHCPv4Error string
// DHCPv4Error implements error interface.
func (d DHCPv4Error) Error() string {
return string(d)
}
const (
// DecOptionNotEnoughData is returned when there is not enough data during option's decode process
DecOptionNotEnoughData = DHCPv4Error("Not enough data to decode")
// DecOptionMalformed is returned when the option is malformed
DecOptionMalformed = DHCPv4Error("Option is malformed")
// InvalidMagicCookie is returned when Magic cookie is missing into BOOTP header
InvalidMagicCookie = DHCPv4Error("Bad DHCP header")
)
+360
View File
@@ -0,0 +1,360 @@
// Copyright 2018 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"
"net"
"github.com/google/gopacket"
)
// DHCPv6MsgType represents a DHCPv6 operation
type DHCPv6MsgType byte
// Constants that represent DHCP operations
const (
DHCPv6MsgTypeUnspecified DHCPv6MsgType = iota
DHCPv6MsgTypeSolicit
DHCPv6MsgTypeAdverstise
DHCPv6MsgTypeRequest
DHCPv6MsgTypeConfirm
DHCPv6MsgTypeRenew
DHCPv6MsgTypeRebind
DHCPv6MsgTypeReply
DHCPv6MsgTypeRelease
DHCPv6MsgTypeDecline
DHCPv6MsgTypeReconfigure
DHCPv6MsgTypeInformationRequest
DHCPv6MsgTypeRelayForward
DHCPv6MsgTypeRelayReply
)
// String returns a string version of a DHCPv6MsgType.
func (o DHCPv6MsgType) String() string {
switch o {
case DHCPv6MsgTypeUnspecified:
return "Unspecified"
case DHCPv6MsgTypeSolicit:
return "Solicit"
case DHCPv6MsgTypeAdverstise:
return "Adverstise"
case DHCPv6MsgTypeRequest:
return "Request"
case DHCPv6MsgTypeConfirm:
return "Confirm"
case DHCPv6MsgTypeRenew:
return "Renew"
case DHCPv6MsgTypeRebind:
return "Rebind"
case DHCPv6MsgTypeReply:
return "Reply"
case DHCPv6MsgTypeRelease:
return "Release"
case DHCPv6MsgTypeDecline:
return "Decline"
case DHCPv6MsgTypeReconfigure:
return "Reconfigure"
case DHCPv6MsgTypeInformationRequest:
return "InformationRequest"
case DHCPv6MsgTypeRelayForward:
return "RelayForward"
case DHCPv6MsgTypeRelayReply:
return "RelayReply"
default:
return "Unknown"
}
}
// DHCPv6 contains data for a single DHCP packet.
type DHCPv6 struct {
BaseLayer
MsgType DHCPv6MsgType
HopCount uint8
LinkAddr net.IP
PeerAddr net.IP
TransactionID []byte
Options DHCPv6Options
}
// LayerType returns gopacket.LayerTypeDHCPv6
func (d *DHCPv6) LayerType() gopacket.LayerType { return LayerTypeDHCPv6 }
// DecodeFromBytes decodes the given bytes into this layer.
func (d *DHCPv6) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
if len(data) < 4 {
df.SetTruncated()
return fmt.Errorf("DHCPv6 length %d too short", len(data))
}
d.BaseLayer = BaseLayer{Contents: data}
d.Options = d.Options[:0]
d.MsgType = DHCPv6MsgType(data[0])
offset := 0
if d.MsgType == DHCPv6MsgTypeRelayForward || d.MsgType == DHCPv6MsgTypeRelayReply {
if len(data) < 34 {
df.SetTruncated()
return fmt.Errorf("DHCPv6 length %d too short for message type %d", len(data), d.MsgType)
}
d.HopCount = data[1]
d.LinkAddr = net.IP(data[2:18])
d.PeerAddr = net.IP(data[18:34])
offset = 34
} else {
d.TransactionID = data[1:4]
offset = 4
}
stop := len(data)
for offset < stop {
o := DHCPv6Option{}
if err := o.decode(data[offset:]); err != nil {
return err
}
d.Options = append(d.Options, o)
offset += int(o.Length) + 4 // 2 from option code, 2 from option length
}
return nil
}
// Len returns the length of a DHCPv6 packet.
func (d *DHCPv6) Len() int {
n := 1
if d.MsgType == DHCPv6MsgTypeRelayForward || d.MsgType == DHCPv6MsgTypeRelayReply {
n += 33
} else {
n += 3
}
for _, o := range d.Options {
n += int(o.Length) + 4 // 2 from option code, 2 from option length
}
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 *DHCPv6) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
plen := int(d.Len())
data, err := b.PrependBytes(plen)
if err != nil {
return err
}
offset := 0
data[0] = byte(d.MsgType)
if d.MsgType == DHCPv6MsgTypeRelayForward || d.MsgType == DHCPv6MsgTypeRelayReply {
data[1] = byte(d.HopCount)
copy(data[2:18], d.LinkAddr.To16())
copy(data[18:34], d.PeerAddr.To16())
offset = 34
} else {
copy(data[1:4], d.TransactionID)
offset = 4
}
if len(d.Options) > 0 {
for _, o := range d.Options {
if err := o.encode(data[offset:], opts); err != nil {
return err
}
offset += int(o.Length) + 4 // 2 from option code, 2 from option length
}
}
return nil
}
// CanDecode returns the set of layer types that this DecodingLayer can decode.
func (d *DHCPv6) CanDecode() gopacket.LayerClass {
return LayerTypeDHCPv6
}
// NextLayerType returns the layer type contained by this DecodingLayer.
func (d *DHCPv6) NextLayerType() gopacket.LayerType {
return gopacket.LayerTypePayload
}
func decodeDHCPv6(data []byte, p gopacket.PacketBuilder) error {
dhcp := &DHCPv6{}
err := dhcp.DecodeFromBytes(data, p)
if err != nil {
return err
}
p.AddLayer(dhcp)
return p.NextDecoder(gopacket.LayerTypePayload)
}
// DHCPv6StatusCode represents a DHCP status code - RFC-3315
type DHCPv6StatusCode uint16
// Constants for the DHCPv6StatusCode.
const (
DHCPv6StatusCodeSuccess DHCPv6StatusCode = iota
DHCPv6StatusCodeUnspecFail
DHCPv6StatusCodeNoAddrsAvail
DHCPv6StatusCodeNoBinding
DHCPv6StatusCodeNotOnLink
DHCPv6StatusCodeUseMulticast
)
// String returns a string version of a DHCPv6StatusCode.
func (o DHCPv6StatusCode) String() string {
switch o {
case DHCPv6StatusCodeSuccess:
return "Success"
case DHCPv6StatusCodeUnspecFail:
return "UnspecifiedFailure"
case DHCPv6StatusCodeNoAddrsAvail:
return "NoAddressAvailable"
case DHCPv6StatusCodeNoBinding:
return "NoBinding"
case DHCPv6StatusCodeNotOnLink:
return "NotOnLink"
case DHCPv6StatusCodeUseMulticast:
return "UseMulticast"
default:
return "Unknown"
}
}
// DHCPv6DUIDType represents a DHCP DUID - RFC-3315
type DHCPv6DUIDType uint16
// Constants for the DHCPv6DUIDType.
const (
DHCPv6DUIDTypeLLT DHCPv6DUIDType = iota + 1
DHCPv6DUIDTypeEN
DHCPv6DUIDTypeLL
)
// String returns a string version of a DHCPv6DUIDType.
func (o DHCPv6DUIDType) String() string {
switch o {
case DHCPv6DUIDTypeLLT:
return "LLT"
case DHCPv6DUIDTypeEN:
return "EN"
case DHCPv6DUIDTypeLL:
return "LL"
default:
return "Unknown"
}
}
// DHCPv6DUID means DHCP Unique Identifier as stated in RFC 3315, section 9 (https://tools.ietf.org/html/rfc3315#page-19)
type DHCPv6DUID struct {
Type DHCPv6DUIDType
// LLT, LL
HardwareType []byte
// EN
EnterpriseNumber []byte
// LLT
Time []byte
// LLT, LL
LinkLayerAddress net.HardwareAddr
// EN
Identifier []byte
}
// DecodeFromBytes decodes the given bytes into a DHCPv6DUID
func (d *DHCPv6DUID) DecodeFromBytes(data []byte) error {
if len(data) < 2 {
return fmt.Errorf("Not enough bytes to decode: %d", len(data))
}
d.Type = DHCPv6DUIDType(binary.BigEndian.Uint16(data[:2]))
if d.Type == DHCPv6DUIDTypeLLT || d.Type == DHCPv6DUIDTypeLL {
if len(data) < 4 {
return fmt.Errorf("Not enough bytes to decode: %d", len(data))
}
d.HardwareType = data[2:4]
}
if d.Type == DHCPv6DUIDTypeLLT {
if len(data) < 8 {
return fmt.Errorf("Not enough bytes to decode: %d", len(data))
}
d.Time = data[4:8]
d.LinkLayerAddress = net.HardwareAddr(data[8:])
} else if d.Type == DHCPv6DUIDTypeEN {
if len(data) < 6 {
return fmt.Errorf("Not enough bytes to decode: %d", len(data))
}
d.EnterpriseNumber = data[2:6]
d.Identifier = data[6:]
} else { // DHCPv6DUIDTypeLL
if len(data) < 4 {
return fmt.Errorf("Not enough bytes to decode: %d", len(data))
}
d.LinkLayerAddress = net.HardwareAddr(data[4:])
}
return nil
}
// Encode encodes the DHCPv6DUID in a slice of bytes
func (d *DHCPv6DUID) Encode() []byte {
length := d.Len()
data := make([]byte, length)
binary.BigEndian.PutUint16(data[0:2], uint16(d.Type))
if d.Type == DHCPv6DUIDTypeLLT || d.Type == DHCPv6DUIDTypeLL {
copy(data[2:4], d.HardwareType)
}
if d.Type == DHCPv6DUIDTypeLLT {
copy(data[4:8], d.Time)
copy(data[8:], d.LinkLayerAddress)
} else if d.Type == DHCPv6DUIDTypeEN {
copy(data[2:6], d.EnterpriseNumber)
copy(data[6:], d.Identifier)
} else {
copy(data[4:], d.LinkLayerAddress)
}
return data
}
// Len returns the length of the DHCPv6DUID, respecting the type
func (d *DHCPv6DUID) Len() int {
length := 2 // d.Type
if d.Type == DHCPv6DUIDTypeLLT {
length += 2 /*HardwareType*/ + 4 /*d.Time*/ + len(d.LinkLayerAddress)
} else if d.Type == DHCPv6DUIDTypeEN {
length += 4 /*d.EnterpriseNumber*/ + len(d.Identifier)
} else { // LL
length += 2 /*d.HardwareType*/ + len(d.LinkLayerAddress)
}
return length
}
func (d *DHCPv6DUID) String() string {
duid := "Type: " + d.Type.String() + ", "
if d.Type == DHCPv6DUIDTypeLLT {
duid += fmt.Sprintf("HardwareType: %v, Time: %v, LinkLayerAddress: %v", d.HardwareType, d.Time, d.LinkLayerAddress)
} else if d.Type == DHCPv6DUIDTypeEN {
duid += fmt.Sprintf("EnterpriseNumber: %v, Identifier: %v", d.EnterpriseNumber, d.Identifier)
} else { // DHCPv6DUIDTypeLL
duid += fmt.Sprintf("HardwareType: %v, LinkLayerAddress: %v", d.HardwareType, d.LinkLayerAddress)
}
return duid
}
func decodeDHCPv6DUID(data []byte) (*DHCPv6DUID, error) {
duid := &DHCPv6DUID{}
err := duid.DecodeFromBytes(data)
if err != nil {
return nil, err
}
return duid, nil
}
+621
View File
@@ -0,0 +1,621 @@
// Copyright 2018 The GoPacket Authors. All rights reserved.
//
// Use of this source code is governed by a BSD-style license
// that can be found in the LICENSE file in the root of the source
// tree.
package layers
import (
"bytes"
"encoding/binary"
"errors"
"fmt"
"github.com/google/gopacket"
)
// DHCPv6Opt represents a DHCP option or parameter from RFC-3315
type DHCPv6Opt uint16
// Constants for the DHCPv6Opt options.
const (
DHCPv6OptClientID DHCPv6Opt = 1
DHCPv6OptServerID DHCPv6Opt = 2
DHCPv6OptIANA DHCPv6Opt = 3
DHCPv6OptIATA DHCPv6Opt = 4
DHCPv6OptIAAddr DHCPv6Opt = 5
DHCPv6OptOro DHCPv6Opt = 6
DHCPv6OptPreference DHCPv6Opt = 7
DHCPv6OptElapsedTime DHCPv6Opt = 8
DHCPv6OptRelayMessage DHCPv6Opt = 9
DHCPv6OptAuth DHCPv6Opt = 11
DHCPv6OptUnicast DHCPv6Opt = 12
DHCPv6OptStatusCode DHCPv6Opt = 13
DHCPv6OptRapidCommit DHCPv6Opt = 14
DHCPv6OptUserClass DHCPv6Opt = 15
DHCPv6OptVendorClass DHCPv6Opt = 16
DHCPv6OptVendorOpts DHCPv6Opt = 17
DHCPv6OptInterfaceID DHCPv6Opt = 18
DHCPv6OptReconfigureMessage DHCPv6Opt = 19
DHCPv6OptReconfigureAccept DHCPv6Opt = 20
// RFC 3319 Session Initiation Protocol (SIP)
DHCPv6OptSIPServersDomainList DHCPv6Opt = 21
DHCPv6OptSIPServersAddressList DHCPv6Opt = 22
// RFC 3646 DNS Configuration
DHCPv6OptDNSServers DHCPv6Opt = 23
DHCPv6OptDomainList DHCPv6Opt = 24
// RFC 3633 Prefix Delegation
DHCPv6OptIAPD DHCPv6Opt = 25
DHCPv6OptIAPrefix DHCPv6Opt = 26
// RFC 3898 Network Information Service (NIS)
DHCPv6OptNISServers DHCPv6Opt = 27
DHCPv6OptNISPServers DHCPv6Opt = 28
DHCPv6OptNISDomainName DHCPv6Opt = 29
DHCPv6OptNISPDomainName DHCPv6Opt = 30
// RFC 4075 Simple Network Time Protocol (SNTP)
DHCPv6OptSNTPServers DHCPv6Opt = 31
// RFC 4242 Information Refresh Time Option
DHCPv6OptInformationRefreshTime DHCPv6Opt = 32
// RFC 4280 Broadcast and Multicast Control Servers
DHCPv6OptBCMCSServerDomainNameList DHCPv6Opt = 33
DHCPv6OptBCMCSServerAddressList DHCPv6Opt = 34
// RFC 4776 Civic Address ConfigurationOption
DHCPv6OptGeoconfCivic DHCPv6Opt = 36
// RFC 4649 Relay Agent Remote-ID
DHCPv6OptRemoteID DHCPv6Opt = 37
// RFC 4580 Relay Agent Subscriber-ID
DHCPv6OptSubscriberID DHCPv6Opt = 38
// RFC 4704 Client Full Qualified Domain Name (FQDN)
DHCPv6OptClientFQDN DHCPv6Opt = 39
// RFC 5192 Protocol for Carrying Authentication for Network Access (PANA)
DHCPv6OptPanaAgent DHCPv6Opt = 40
// RFC 4833 Timezone Options
DHCPv6OptNewPOSIXTimezone DHCPv6Opt = 41
DHCPv6OptNewTZDBTimezone DHCPv6Opt = 42
// RFC 4994 Relay Agent Echo Request
DHCPv6OptEchoRequestOption DHCPv6Opt = 43
// RFC 5007 Leasequery
DHCPv6OptLQQuery DHCPv6Opt = 44
DHCPv6OptCLTTime DHCPv6Opt = 45
DHCPv6OptClientData DHCPv6Opt = 46
DHCPv6OptLQRelayData DHCPv6Opt = 47
DHCPv6OptLQClientLink DHCPv6Opt = 48
// RFC 6610 Home Information Discovery in Mobile IPv6 (MIPv6)
DHCPv6OptMIP6HNIDF DHCPv6Opt = 49
DHCPv6OptMIP6VDINF DHCPv6Opt = 50
DHCPv6OptMIP6IDINF DHCPv6Opt = 69
DHCPv6OptMIP6UDINF DHCPv6Opt = 70
DHCPv6OptMIP6HNP DHCPv6Opt = 71
DHCPv6OptMIP6HAA DHCPv6Opt = 72
DHCPv6OptMIP6HAF DHCPv6Opt = 73
// RFC 5223 Discovering Location-to-Service Translation (LoST) Servers
DHCPv6OptV6LOST DHCPv6Opt = 51
// RFC 5417 Control And Provisioning of Wireless Access Points (CAPWAP)
DHCPv6OptCAPWAPACV6 DHCPv6Opt = 52
// RFC 5460 Bulk Leasequery
DHCPv6OptRelayID DHCPv6Opt = 53
// RFC 5678 IEEE 802.21 Mobility Services (MoS) Discovery
DHCPv6OptIPv6AddressMoS DHCPv6Opt = 54
DHCPv6OptIPv6FQDNMoS DHCPv6Opt = 55
// RFC 5908 NTP Server Option
DHCPv6OptNTPServer DHCPv6Opt = 56
// RFC 5986 Discovering the Local Location Information Server (LIS)
DHCPv6OptV6AccessDomain DHCPv6Opt = 57
// RFC 5986 SIP User Agent
DHCPv6OptSIPUACSList DHCPv6Opt = 58
// RFC 5970 Options for Network Boot
DHCPv6OptBootFileURL DHCPv6Opt = 59
DHCPv6OptBootFileParam DHCPv6Opt = 60
DHCPv6OptClientArchType DHCPv6Opt = 61
DHCPv6OptNII DHCPv6Opt = 62
// RFC 6225 Coordinate-Based Location Configuration Information
DHCPv6OptGeolocation DHCPv6Opt = 63
// RFC 6334 Dual-Stack Lite
DHCPv6OptAFTRName DHCPv6Opt = 64
// RFC 6440 EAP Re-authentication Protocol (ERP)
DHCPv6OptERPLocalDomainName DHCPv6Opt = 65
// RFC 6422 Relay-Supplied DHCP Options
DHCPv6OptRSOO DHCPv6Opt = 66
// RFC 6603 Prefix Exclude Option for DHCPv6-based Prefix Delegation
DHCPv6OptPDExclude DHCPv6Opt = 67
// RFC 6607 Virtual Subnet Selection
DHCPv6OptVSS DHCPv6Opt = 68
// RFC 6731 Improved Recursive DNS Server Selection for Multi-Interfaced Nodes
DHCPv6OptRDNSSSelection DHCPv6Opt = 74
// RFC 6784 Kerberos Options for DHCPv6
DHCPv6OptKRBPrincipalName DHCPv6Opt = 75
DHCPv6OptKRBRealmName DHCPv6Opt = 76
DHCPv6OptKRBKDC DHCPv6Opt = 77
// RFC 6939 Client Link-Layer Address Option
DHCPv6OptClientLinkLayerAddress DHCPv6Opt = 79
// RFC 6977 Triggering DHCPv6 Reconfiguration from Relay Agents
DHCPv6OptLinkAddress DHCPv6Opt = 80
// RFC 7037 RADIUS Option for the DHCPv6 Relay Agent
DHCPv6OptRADIUS DHCPv6Opt = 81
// RFC 7083 Modification to Default Values of SOL_MAX_RT and INF_MAX_RT
DHCPv6OptSolMaxRt DHCPv6Opt = 82
DHCPv6OptInfMaxRt DHCPv6Opt = 83
// RFC 7078 Distributing Address Selection Policy
DHCPv6OptAddrSel DHCPv6Opt = 84
DHCPv6OptAddrSelTable DHCPv6Opt = 85
// RFC 7291 DHCP Options for the Port Control Protocol (PCP)
DHCPv6OptV6PCPServer DHCPv6Opt = 86
// RFC 7341 DHCPv4-over-DHCPv6 (DHCP 4o6) Transport
DHCPv6OptDHCPv4Message DHCPv6Opt = 87
DHCPv6OptDHCPv4OverDHCPv6Server DHCPv6Opt = 88
// RFC 7598 Configuration of Softwire Address and Port-Mapped Clients
DHCPv6OptS46Rule DHCPv6Opt = 89
DHCPv6OptS46BR DHCPv6Opt = 90
DHCPv6OptS46DMR DHCPv6Opt = 91
DHCPv6OptS46V4V4Bind DHCPv6Opt = 92
DHCPv6OptS46PortParameters DHCPv6Opt = 93
DHCPv6OptS46ContMAPE DHCPv6Opt = 94
DHCPv6OptS46ContMAPT DHCPv6Opt = 95
DHCPv6OptS46ContLW DHCPv6Opt = 96
// RFC 7600 IPv4 Residual Deployment via IPv6
DHCPv6Opt4RD DHCPv6Opt = 97
DHCPv6Opt4RDMapRule DHCPv6Opt = 98
DHCPv6Opt4RDNonMapRule DHCPv6Opt = 99
// RFC 7653 Active Leasequery
DHCPv6OptLQBaseTime DHCPv6Opt = 100
DHCPv6OptLQStartTime DHCPv6Opt = 101
DHCPv6OptLQEndTime DHCPv6Opt = 102
// RFC 7710 Captive-Portal Identification
DHCPv6OptCaptivePortal DHCPv6Opt = 103
// RFC 7774 Multicast Protocol for Low-Power and Lossy Networks (MPL) Parameter Configuration
DHCPv6OptMPLParameters DHCPv6Opt = 104
// RFC 7839 Access-Network-Identifier (ANI)
DHCPv6OptANIATT DHCPv6Opt = 105
DHCPv6OptANINetworkName DHCPv6Opt = 106
DHCPv6OptANIAPName DHCPv6Opt = 107
DHCPv6OptANIAPBSSID DHCPv6Opt = 108
DHCPv6OptANIOperatorID DHCPv6Opt = 109
DHCPv6OptANIOperatorRealm DHCPv6Opt = 110
// RFC 8026 Unified IPv4-in-IPv6 Softwire Customer Premises Equipment (CPE)
DHCPv6OptS46Priority DHCPv6Opt = 111
// draft-ietf-opsawg-mud-25 Manufacturer Usage Description (MUD)
DHCPv6OptMUDURLV6 DHCPv6Opt = 112
// RFC 8115 IPv4-Embedded Multicast and Unicast IPv6 Prefixes
DHCPv6OptV6Prefix64 DHCPv6Opt = 113
// RFC 8156 DHCPv6 Failover Protocol
DHCPv6OptFBindingStatus DHCPv6Opt = 114
DHCPv6OptFConnectFlags DHCPv6Opt = 115
DHCPv6OptFDNSRemovalInfo DHCPv6Opt = 116
DHCPv6OptFDNSHostName DHCPv6Opt = 117
DHCPv6OptFDNSZoneName DHCPv6Opt = 118
DHCPv6OptFDNSFlags DHCPv6Opt = 119
DHCPv6OptFExpirationTime DHCPv6Opt = 120
DHCPv6OptFMaxUnacknowledgedBNDUPD DHCPv6Opt = 121
DHCPv6OptFMCLT DHCPv6Opt = 122
DHCPv6OptFPartnerLifetime DHCPv6Opt = 123
DHCPv6OptFPartnerLifetimeSent DHCPv6Opt = 124
DHCPv6OptFPartnerDownTime DHCPv6Opt = 125
DHCPv6OptFPartnerRawCltTime DHCPv6Opt = 126
DHCPv6OptFProtocolVersion DHCPv6Opt = 127
DHCPv6OptFKeepaliveTime DHCPv6Opt = 128
DHCPv6OptFReconfigureData DHCPv6Opt = 129
DHCPv6OptFRelationshipName DHCPv6Opt = 130
DHCPv6OptFServerFlags DHCPv6Opt = 131
DHCPv6OptFServerState DHCPv6Opt = 132
DHCPv6OptFStartTimeOfState DHCPv6Opt = 133
DHCPv6OptFStateExpirationTime DHCPv6Opt = 134
// RFC 8357 Generalized UDP Source Port for DHCP Relay
DHCPv6OptRelayPort DHCPv6Opt = 135
// draft-ietf-netconf-zerotouch-25 Zero Touch Provisioning for Networking Devices
DHCPv6OptV6ZeroTouchRedirect DHCPv6Opt = 136
// RFC 6153 Access Network Discovery and Selection Function (ANDSF) Discovery
DHCPv6OptIPV6AddressANDSF DHCPv6Opt = 143
)
// String returns a string version of a DHCPv6Opt.
func (o DHCPv6Opt) String() string {
switch o {
case DHCPv6OptClientID:
return "ClientID"
case DHCPv6OptServerID:
return "ServerID"
case DHCPv6OptIANA:
return "IA_NA"
case DHCPv6OptIATA:
return "IA_TA"
case DHCPv6OptIAAddr:
return "IAAddr"
case DHCPv6OptOro:
return "Oro"
case DHCPv6OptPreference:
return "Preference"
case DHCPv6OptElapsedTime:
return "ElapsedTime"
case DHCPv6OptRelayMessage:
return "RelayMessage"
case DHCPv6OptAuth:
return "Auth"
case DHCPv6OptUnicast:
return "Unicast"
case DHCPv6OptStatusCode:
return "StatusCode"
case DHCPv6OptRapidCommit:
return "RapidCommit"
case DHCPv6OptUserClass:
return "UserClass"
case DHCPv6OptVendorClass:
return "VendorClass"
case DHCPv6OptVendorOpts:
return "VendorOpts"
case DHCPv6OptInterfaceID:
return "InterfaceID"
case DHCPv6OptReconfigureMessage:
return "ReconfigureMessage"
case DHCPv6OptReconfigureAccept:
return "ReconfigureAccept"
case DHCPv6OptSIPServersDomainList:
return "SIPServersDomainList"
case DHCPv6OptSIPServersAddressList:
return "SIPServersAddressList"
case DHCPv6OptDNSServers:
return "DNSRecursiveNameServer"
case DHCPv6OptDomainList:
return "DomainSearchList"
case DHCPv6OptIAPD:
return "IdentityAssociationPrefixDelegation"
case DHCPv6OptIAPrefix:
return "IAPDPrefix"
case DHCPv6OptNISServers:
return "NISServers"
case DHCPv6OptNISPServers:
return "NISv2Servers"
case DHCPv6OptNISDomainName:
return "NISDomainName"
case DHCPv6OptNISPDomainName:
return "NISv2DomainName"
case DHCPv6OptSNTPServers:
return "SNTPServers"
case DHCPv6OptInformationRefreshTime:
return "InformationRefreshTime"
case DHCPv6OptBCMCSServerDomainNameList:
return "BCMCSControlServersDomainNameList"
case DHCPv6OptBCMCSServerAddressList:
return "BCMCSControlServersAddressList"
case DHCPv6OptGeoconfCivic:
return "CivicAddress"
case DHCPv6OptRemoteID:
return "RelayAgentRemoteID"
case DHCPv6OptSubscriberID:
return "RelayAgentSubscriberID"
case DHCPv6OptClientFQDN:
return "ClientFQDN"
case DHCPv6OptPanaAgent:
return "PANAAuthenticationAgent"
case DHCPv6OptNewPOSIXTimezone:
return "NewPOSIXTimezone"
case DHCPv6OptNewTZDBTimezone:
return "NewTZDBTimezone"
case DHCPv6OptEchoRequestOption:
return "EchoRequest"
case DHCPv6OptLQQuery:
return "LeasequeryQuery"
case DHCPv6OptClientData:
return "LeasequeryClientData"
case DHCPv6OptCLTTime:
return "LeasequeryClientLastTransactionTime"
case DHCPv6OptLQRelayData:
return "LeasequeryRelayData"
case DHCPv6OptLQClientLink:
return "LeasequeryClientLink"
case DHCPv6OptMIP6HNIDF:
return "MIPv6HomeNetworkIDFQDN"
case DHCPv6OptMIP6VDINF:
return "MIPv6VisitedHomeNetworkInformation"
case DHCPv6OptMIP6IDINF:
return "MIPv6IdentifiedHomeNetworkInformation"
case DHCPv6OptMIP6UDINF:
return "MIPv6UnrestrictedHomeNetworkInformation"
case DHCPv6OptMIP6HNP:
return "MIPv6HomeNetworkPrefix"
case DHCPv6OptMIP6HAA:
return "MIPv6HomeAgentAddress"
case DHCPv6OptMIP6HAF:
return "MIPv6HomeAgentFQDN"
case DHCPv6OptV6LOST:
return "LoST Server"
case DHCPv6OptCAPWAPACV6:
return "CAPWAPAccessControllerV6"
case DHCPv6OptRelayID:
return "LeasequeryRelayID"
case DHCPv6OptIPv6AddressMoS:
return "MoSIPv6Address"
case DHCPv6OptIPv6FQDNMoS:
return "MoSDomainNameList"
case DHCPv6OptNTPServer:
return "NTPServer"
case DHCPv6OptV6AccessDomain:
return "AccessNetworkDomainName"
case DHCPv6OptSIPUACSList:
return "SIPUserAgentConfigurationServiceDomains"
case DHCPv6OptBootFileURL:
return "BootFileURL"
case DHCPv6OptBootFileParam:
return "BootFileParameters"
case DHCPv6OptClientArchType:
return "ClientSystemArchitectureType"
case DHCPv6OptNII:
return "ClientNetworkInterfaceIdentifier"
case DHCPv6OptGeolocation:
return "Geolocation"
case DHCPv6OptAFTRName:
return "AFTRName"
case DHCPv6OptERPLocalDomainName:
return "AFTRName"
case DHCPv6OptRSOO:
return "RSOOption"
case DHCPv6OptPDExclude:
return "PrefixExclude"
case DHCPv6OptVSS:
return "VirtualSubnetSelection"
case DHCPv6OptRDNSSSelection:
return "RDNSSSelection"
case DHCPv6OptKRBPrincipalName:
return "KerberosPrincipalName"
case DHCPv6OptKRBRealmName:
return "KerberosRealmName"
case DHCPv6OptKRBKDC:
return "KerberosKDC"
case DHCPv6OptClientLinkLayerAddress:
return "ClientLinkLayerAddress"
case DHCPv6OptLinkAddress:
return "LinkAddress"
case DHCPv6OptRADIUS:
return "RADIUS"
case DHCPv6OptSolMaxRt:
return "SolMaxRt"
case DHCPv6OptInfMaxRt:
return "InfMaxRt"
case DHCPv6OptAddrSel:
return "AddressSelection"
case DHCPv6OptAddrSelTable:
return "AddressSelectionTable"
case DHCPv6OptV6PCPServer:
return "PCPServer"
case DHCPv6OptDHCPv4Message:
return "DHCPv4Message"
case DHCPv6OptDHCPv4OverDHCPv6Server:
return "DHCP4o6ServerAddress"
case DHCPv6OptS46Rule:
return "S46Rule"
case DHCPv6OptS46BR:
return "S46BR"
case DHCPv6OptS46DMR:
return "S46DMR"
case DHCPv6OptS46V4V4Bind:
return "S46IPv4IPv6AddressBinding"
case DHCPv6OptS46PortParameters:
return "S46PortParameters"
case DHCPv6OptS46ContMAPE:
return "S46MAPEContainer"
case DHCPv6OptS46ContMAPT:
return "S46MAPTContainer"
case DHCPv6OptS46ContLW:
return "S46Lightweight4Over6Container"
case DHCPv6Opt4RD:
return "4RD"
case DHCPv6Opt4RDMapRule:
return "4RDMapRule"
case DHCPv6Opt4RDNonMapRule:
return "4RDNonMapRule"
case DHCPv6OptLQBaseTime:
return "LQBaseTime"
case DHCPv6OptLQStartTime:
return "LQStartTime"
case DHCPv6OptLQEndTime:
return "LQEndTime"
case DHCPv6OptCaptivePortal:
return "CaptivePortal"
case DHCPv6OptMPLParameters:
return "MPLParameterConfiguration"
case DHCPv6OptANIATT:
return "ANIAccessTechnologyType"
case DHCPv6OptANINetworkName:
return "ANINetworkName"
case DHCPv6OptANIAPName:
return "ANIAccessPointName"
case DHCPv6OptANIAPBSSID:
return "ANIAccessPointBSSID"
case DHCPv6OptANIOperatorID:
return "ANIOperatorIdentifier"
case DHCPv6OptANIOperatorRealm:
return "ANIOperatorRealm"
case DHCPv6OptS46Priority:
return "S64Priority"
case DHCPv6OptMUDURLV6:
return "ManufacturerUsageDescriptionURL"
case DHCPv6OptV6Prefix64:
return "V6Prefix64"
case DHCPv6OptFBindingStatus:
return "FailoverBindingStatus"
case DHCPv6OptFConnectFlags:
return "FailoverConnectFlags"
case DHCPv6OptFDNSRemovalInfo:
return "FailoverDNSRemovalInfo"
case DHCPv6OptFDNSHostName:
return "FailoverDNSHostName"
case DHCPv6OptFDNSZoneName:
return "FailoverDNSZoneName"
case DHCPv6OptFDNSFlags:
return "FailoverDNSFlags"
case DHCPv6OptFExpirationTime:
return "FailoverExpirationTime"
case DHCPv6OptFMaxUnacknowledgedBNDUPD:
return "FailoverMaxUnacknowledgedBNDUPDMessages"
case DHCPv6OptFMCLT:
return "FailoverMaximumClientLeadTime"
case DHCPv6OptFPartnerLifetime:
return "FailoverPartnerLifetime"
case DHCPv6OptFPartnerLifetimeSent:
return "FailoverPartnerLifetimeSent"
case DHCPv6OptFPartnerDownTime:
return "FailoverPartnerDownTime"
case DHCPv6OptFPartnerRawCltTime:
return "FailoverPartnerRawClientLeadTime"
case DHCPv6OptFProtocolVersion:
return "FailoverProtocolVersion"
case DHCPv6OptFKeepaliveTime:
return "FailoverKeepaliveTime"
case DHCPv6OptFReconfigureData:
return "FailoverReconfigureData"
case DHCPv6OptFRelationshipName:
return "FailoverRelationshipName"
case DHCPv6OptFServerFlags:
return "FailoverServerFlags"
case DHCPv6OptFServerState:
return "FailoverServerState"
case DHCPv6OptFStartTimeOfState:
return "FailoverStartTimeOfState"
case DHCPv6OptFStateExpirationTime:
return "FailoverStateExpirationTime"
case DHCPv6OptRelayPort:
return "RelayPort"
case DHCPv6OptV6ZeroTouchRedirect:
return "ZeroTouch"
case DHCPv6OptIPV6AddressANDSF:
return "ANDSFIPv6Address"
default:
return fmt.Sprintf("Unknown(%d)", uint16(o))
}
}
// DHCPv6Options is used to get nicely printed option lists which would normally
// be cut off after 5 options.
type DHCPv6Options []DHCPv6Option
// String returns a string version of the options list.
func (o DHCPv6Options) String() string {
buf := &bytes.Buffer{}
buf.WriteByte('[')
for i, opt := range o {
buf.WriteString(opt.String())
if i+1 != len(o) {
buf.WriteString(", ")
}
}
buf.WriteByte(']')
return buf.String()
}
// DHCPv6Option rerpresents a DHCP option.
type DHCPv6Option struct {
Code DHCPv6Opt
Length uint16
Data []byte
}
// String returns a string version of a DHCP Option.
func (o DHCPv6Option) String() string {
switch o.Code {
case DHCPv6OptClientID, DHCPv6OptServerID:
duid, err := decodeDHCPv6DUID(o.Data)
if err != nil {
return fmt.Sprintf("Option(%s:INVALID)", o.Code)
}
return fmt.Sprintf("Option(%s:[%s])", o.Code, duid.String())
case DHCPv6OptOro:
options := ""
for i := 0; i < int(o.Length); i += 2 {
if options != "" {
options += ","
}
option := DHCPv6Opt(binary.BigEndian.Uint16(o.Data[i : i+2]))
options += option.String()
}
return fmt.Sprintf("Option(%s:[%s])", o.Code, options)
default:
return fmt.Sprintf("Option(%s:%v)", o.Code, o.Data)
}
}
// NewDHCPv6Option constructs a new DHCPv6Option with a given type and data.
func NewDHCPv6Option(code DHCPv6Opt, data []byte) DHCPv6Option {
o := DHCPv6Option{Code: code}
if data != nil {
o.Data = data
o.Length = uint16(len(data))
}
return o
}
func (o *DHCPv6Option) encode(b []byte, opts gopacket.SerializeOptions) error {
binary.BigEndian.PutUint16(b[0:2], uint16(o.Code))
if opts.FixLengths {
binary.BigEndian.PutUint16(b[2:4], uint16(len(o.Data)))
} else {
binary.BigEndian.PutUint16(b[2:4], o.Length)
}
copy(b[4:], o.Data)
return nil
}
func (o *DHCPv6Option) decode(data []byte) error {
if len(data) < 4 {
return errors.New("not enough data to decode")
}
o.Code = DHCPv6Opt(binary.BigEndian.Uint16(data[0:2]))
o.Length = binary.BigEndian.Uint16(data[2:4])
if len(data) < 4+int(o.Length) {
return fmt.Errorf("dhcpv6 option size < length %d", 4+o.Length)
}
o.Data = data[4 : 4+o.Length]
return nil
}
+1098
View File
File diff suppressed because it is too large Load Diff
+61
View File
@@ -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
+2118
View File
File diff suppressed because it is too large Load Diff
+75
View File
@@ -0,0 +1,75 @@
// 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 {
if len(data) < 4 {
df.SetTruncated()
return fmt.Errorf("802.1Q tag length %d too short", len(data))
}
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
}
+114
View File
@@ -0,0 +1,114 @@
// 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 {
if len(data) < 4 {
df.SetTruncated()
return fmt.Errorf("EAP length %d too short", len(data))
}
e.Code = EAPCode(data[0])
e.Id = data[1]
e.Length = binary.BigEndian.Uint16(data[2:4])
if len(data) < int(e.Length) {
df.SetTruncated()
return fmt.Errorf("EAP length %d too short, %d expected", len(data), e.Length)
}
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)
}
+302
View File
@@ -0,0 +1,302 @@
// 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"
)
// 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 {
if len(data) < 4 {
df.SetTruncated()
return fmt.Errorf("EAPOL length %d too short", len(data))
}
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)
}
// EAPOLKeyDescriptorType is an enumeration of key descriptor types
// as specified by 802.1x in the EAPOL-Key frame
type EAPOLKeyDescriptorType uint8
// Enumeration of EAPOLKeyDescriptorType
const (
EAPOLKeyDescriptorTypeRC4 EAPOLKeyDescriptorType = 1
EAPOLKeyDescriptorTypeDot11 EAPOLKeyDescriptorType = 2
EAPOLKeyDescriptorTypeWPA EAPOLKeyDescriptorType = 254
)
func (kdt EAPOLKeyDescriptorType) String() string {
switch kdt {
case EAPOLKeyDescriptorTypeRC4:
return "RC4"
case EAPOLKeyDescriptorTypeDot11:
return "802.11"
case EAPOLKeyDescriptorTypeWPA:
return "WPA"
default:
return fmt.Sprintf("unknown descriptor type %d", kdt)
}
}
// EAPOLKeyDescriptorVersion is an enumeration of versions specifying the
// encryption algorithm for the key data and the authentication for the
// message integrity code (MIC)
type EAPOLKeyDescriptorVersion uint8
// Enumeration of EAPOLKeyDescriptorVersion
const (
EAPOLKeyDescriptorVersionOther EAPOLKeyDescriptorVersion = 0
EAPOLKeyDescriptorVersionRC4HMACMD5 EAPOLKeyDescriptorVersion = 1
EAPOLKeyDescriptorVersionAESHMACSHA1 EAPOLKeyDescriptorVersion = 2
EAPOLKeyDescriptorVersionAES128CMAC EAPOLKeyDescriptorVersion = 3
)
func (v EAPOLKeyDescriptorVersion) String() string {
switch v {
case EAPOLKeyDescriptorVersionOther:
return "Other"
case EAPOLKeyDescriptorVersionRC4HMACMD5:
return "RC4-HMAC-MD5"
case EAPOLKeyDescriptorVersionAESHMACSHA1:
return "AES-HMAC-SHA1-128"
case EAPOLKeyDescriptorVersionAES128CMAC:
return "AES-128-CMAC"
default:
return fmt.Sprintf("unknown version %d", v)
}
}
// EAPOLKeyType is an enumeration of key derivation types describing
// the purpose of the keys being derived.
type EAPOLKeyType uint8
// Enumeration of EAPOLKeyType
const (
EAPOLKeyTypeGroupSMK EAPOLKeyType = 0
EAPOLKeyTypePairwise EAPOLKeyType = 1
)
func (kt EAPOLKeyType) String() string {
switch kt {
case EAPOLKeyTypeGroupSMK:
return "Group/SMK"
case EAPOLKeyTypePairwise:
return "Pairwise"
default:
return fmt.Sprintf("unknown key type %d", kt)
}
}
// EAPOLKey defines an EAPOL-Key frame for 802.1x authentication
type EAPOLKey struct {
BaseLayer
KeyDescriptorType EAPOLKeyDescriptorType
KeyDescriptorVersion EAPOLKeyDescriptorVersion
KeyType EAPOLKeyType
KeyIndex uint8
Install bool
KeyACK bool
KeyMIC bool
Secure bool
MICError bool
Request bool
HasEncryptedKeyData bool
SMKMessage bool
KeyLength uint16
ReplayCounter uint64
Nonce []byte
IV []byte
RSC uint64
ID uint64
MIC []byte
KeyDataLength uint16
EncryptedKeyData []byte
}
// LayerType returns LayerTypeEAPOLKey.
func (ek *EAPOLKey) LayerType() gopacket.LayerType {
return LayerTypeEAPOLKey
}
// CanDecode returns the set of layer types that this DecodingLayer can decode.
func (ek *EAPOLKey) CanDecode() gopacket.LayerType {
return LayerTypeEAPOLKey
}
// NextLayerType returns layers.LayerTypeDot11InformationElement if the key
// data exists and is unencrypted, otherwise it does not expect a next layer.
func (ek *EAPOLKey) NextLayerType() gopacket.LayerType {
if !ek.HasEncryptedKeyData && ek.KeyDataLength > 0 {
return LayerTypeDot11InformationElement
}
return gopacket.LayerTypePayload
}
const eapolKeyFrameLen = 95
// DecodeFromBytes decodes the given bytes into this layer.
func (ek *EAPOLKey) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
if len(data) < eapolKeyFrameLen {
df.SetTruncated()
return fmt.Errorf("EAPOLKey length %v too short, %v required",
len(data), eapolKeyFrameLen)
}
ek.KeyDescriptorType = EAPOLKeyDescriptorType(data[0])
info := binary.BigEndian.Uint16(data[1:3])
ek.KeyDescriptorVersion = EAPOLKeyDescriptorVersion(info & 0x0007)
ek.KeyType = EAPOLKeyType((info & 0x0008) >> 3)
ek.KeyIndex = uint8((info & 0x0030) >> 4)
ek.Install = (info & 0x0040) != 0
ek.KeyACK = (info & 0x0080) != 0
ek.KeyMIC = (info & 0x0100) != 0
ek.Secure = (info & 0x0200) != 0
ek.MICError = (info & 0x0400) != 0
ek.Request = (info & 0x0800) != 0
ek.HasEncryptedKeyData = (info & 0x1000) != 0
ek.SMKMessage = (info & 0x2000) != 0
ek.KeyLength = binary.BigEndian.Uint16(data[3:5])
ek.ReplayCounter = binary.BigEndian.Uint64(data[5:13])
ek.Nonce = data[13:45]
ek.IV = data[45:61]
ek.RSC = binary.BigEndian.Uint64(data[61:69])
ek.ID = binary.BigEndian.Uint64(data[69:77])
ek.MIC = data[77:93]
ek.KeyDataLength = binary.BigEndian.Uint16(data[93:95])
totalLength := eapolKeyFrameLen + int(ek.KeyDataLength)
if len(data) < totalLength {
df.SetTruncated()
return fmt.Errorf("EAPOLKey data length %d too short, %d required",
len(data)-eapolKeyFrameLen, ek.KeyDataLength)
}
if ek.HasEncryptedKeyData {
ek.EncryptedKeyData = data[eapolKeyFrameLen:totalLength]
ek.BaseLayer = BaseLayer{
Contents: data[:totalLength],
Payload: data[totalLength:],
}
} else {
ek.BaseLayer = BaseLayer{
Contents: data[:eapolKeyFrameLen],
Payload: data[eapolKeyFrameLen:],
}
}
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 (ek *EAPOLKey) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
buf, err := b.PrependBytes(eapolKeyFrameLen + len(ek.EncryptedKeyData))
if err != nil {
return err
}
buf[0] = byte(ek.KeyDescriptorType)
var info uint16
info |= uint16(ek.KeyDescriptorVersion)
info |= uint16(ek.KeyType) << 3
info |= uint16(ek.KeyIndex) << 4
if ek.Install {
info |= 0x0040
}
if ek.KeyACK {
info |= 0x0080
}
if ek.KeyMIC {
info |= 0x0100
}
if ek.Secure {
info |= 0x0200
}
if ek.MICError {
info |= 0x0400
}
if ek.Request {
info |= 0x0800
}
if ek.HasEncryptedKeyData {
info |= 0x1000
}
if ek.SMKMessage {
info |= 0x2000
}
binary.BigEndian.PutUint16(buf[1:3], info)
binary.BigEndian.PutUint16(buf[3:5], ek.KeyLength)
binary.BigEndian.PutUint64(buf[5:13], ek.ReplayCounter)
copy(buf[13:45], ek.Nonce)
copy(buf[45:61], ek.IV)
binary.BigEndian.PutUint64(buf[61:69], ek.RSC)
binary.BigEndian.PutUint64(buf[69:77], ek.ID)
copy(buf[77:93], ek.MIC)
binary.BigEndian.PutUint16(buf[93:95], ek.KeyDataLength)
if len(ek.EncryptedKeyData) > 0 {
copy(buf[95:95+len(ek.EncryptedKeyData)], ek.EncryptedKeyData)
}
return nil
}
func decodeEAPOLKey(data []byte, p gopacket.PacketBuilder) error {
ek := &EAPOLKey{}
return decodingLayerDecoder(ek, data, p)
}
+97
View File
@@ -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))
}
+443
View File
@@ -0,0 +1,443 @@
// 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 (
"fmt"
"runtime"
"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
}
// 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
EthernetTypePPP EthernetType = 0x880b
EthernetTypePPPoEDiscovery EthernetType = 0x8863
EthernetTypePPPoESession EthernetType = 0x8864
EthernetTypeMPLSUnicast EthernetType = 0x8847
EthernetTypeMPLSMulticast EthernetType = 0x8848
EthernetTypeEAPOL EthernetType = 0x888e
EthernetTypeERSPAN EthernetType = 0x88be
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
IPProtocolOSPF IPProtocol = 89
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
LinkTypeFC2 LinkType = 224
LinkTypeFC2Framed LinkType = 225
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
}
func (d Dot11Type) QOS() bool {
return d&dot11QOSMask == Dot11TypeDataQOSData
}
const (
Dot11TypeMgmt Dot11Type = 0x00
Dot11TypeCtrl Dot11Type = 0x01
Dot11TypeData Dot11Type = 0x02
Dot11TypeReserved Dot11Type = 0x03
dot11TypeMask = 0x03
dot11QOSMask = 0x23
// 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
)
// 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 initActualTypeData() {
// Each of the XXXTypeMetadata arrays contains mappings of how to handle enum
// values for various enum types in gopacket/layers.
// These arrays are actually created by gen2.go and stored in
// enums_generated.go.
//
// 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.
// Here we link up all enumerations with their respective names and decoders.
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[EthernetTypePPP] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodePPP), Name: "PPP", LayerType: LayerTypePPP}
EthernetTypeMetadata[EthernetTypePPPoEDiscovery] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodePPPoE), Name: "PPPoEDiscovery", LayerType: LayerTypePPPoE}
EthernetTypeMetadata[EthernetTypePPPoESession] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodePPPoE), Name: "PPPoESession", LayerType: LayerTypePPPoE}
EthernetTypeMetadata[EthernetTypeEthernetCTP] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeEthernetCTP), Name: "EthernetCTP", LayerType: LayerTypeEthernetCTP}
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}
EthernetTypeMetadata[EthernetTypeERSPAN] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeERSPANII), Name: "ERSPAN Type II", LayerType: LayerTypeERSPANII}
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[IPProtocolOSPF] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeOSPF), Name: "OSPF", LayerType: LayerTypeOSPF}
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"}
// See https://github.com/the-tcpdump-group/libpcap/blob/170f717e6e818cdc4bcbbfd906b63088eaa88fa0/pcap/dlt.h#L85
// Or https://github.com/wireshark/wireshark/blob/854cfe53efe44080609c78053ecfb2342ad84a08/wiretap/pcap-common.c#L508
if runtime.GOOS == "openbsd" {
LinkTypeMetadata[14] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeIPv4or6), Name: "Raw"}
} else {
LinkTypeMetadata[12] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeIPv4or6), Name: "Raw"}
}
LinkTypeMetadata[LinkTypePFLog] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodePFLog), Name: "PFLog"}
LinkTypeMetadata[LinkTypeIEEE80211Radio] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeRadioTap), Name: "RadioTap"}
LinkTypeMetadata[LinkTypeLinuxUSB] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeUSB), Name: "USB"}
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}
EAPOLTypeMetadata[EAPOLTypeKey] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeEAPOLKey), Name: "EAPOLKey", LayerType: LayerTypeEAPOLKey}
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}
USBTransportTypeMetadata[USBTransportTypeInterrupt] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeUSBInterrupt), Name: "Interrupt", LayerType: LayerTypeUSBInterrupt}
USBTransportTypeMetadata[USBTransportTypeControl] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeUSBControl), Name: "Control", LayerType: LayerTypeUSBControl}
USBTransportTypeMetadata[USBTransportTypeBulk] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeUSBBulk), Name: "Bulk", LayerType: LayerTypeUSBBulk}
}
+434
View File
@@ -0,0 +1,434 @@
// Copyright 2012 Google, Inc. All rights reserved.
package layers
// Created by gen2.go, don't edit manually
// Generated at 2017-10-23 10:20:24.458771856 -0600 MDT m=+0.001159033
import (
"fmt"
"github.com/google/gopacket"
)
func init() {
initUnknownTypesForLinkType()
initUnknownTypesForEthernetType()
initUnknownTypesForPPPType()
initUnknownTypesForIPProtocol()
initUnknownTypesForSCTPChunkType()
initUnknownTypesForPPPoECode()
initUnknownTypesForFDDIFrameControl()
initUnknownTypesForEAPOLType()
initUnknownTypesForProtocolFamily()
initUnknownTypesForDot11Type()
initUnknownTypesForUSBTransportType()
initActualTypeData()
}
// Decoder calls LinkTypeMetadata.DecodeWith's decoder.
func (a LinkType) Decode(data []byte, p gopacket.PacketBuilder) error {
return LinkTypeMetadata[a].DecodeWith.Decode(data, p)
}
// String returns LinkTypeMetadata.Name.
func (a LinkType) String() string {
return LinkTypeMetadata[a].Name
}
// LayerType returns LinkTypeMetadata.LayerType.
func (a LinkType) LayerType() gopacket.LayerType {
return LinkTypeMetadata[a].LayerType
}
type errorDecoderForLinkType int
func (a *errorDecoderForLinkType) Decode(data []byte, p gopacket.PacketBuilder) error {
return a
}
func (a *errorDecoderForLinkType) Error() string {
return fmt.Sprintf("Unable to decode LinkType %d", int(*a))
}
var errorDecodersForLinkType [256]errorDecoderForLinkType
var LinkTypeMetadata [256]EnumMetadata
func initUnknownTypesForLinkType() {
for i := 0; i < 256; i++ {
errorDecodersForLinkType[i] = errorDecoderForLinkType(i)
LinkTypeMetadata[i] = EnumMetadata{
DecodeWith: &errorDecodersForLinkType[i],
Name: "UnknownLinkType",
}
}
}
// Decoder calls EthernetTypeMetadata.DecodeWith's decoder.
func (a EthernetType) Decode(data []byte, p gopacket.PacketBuilder) error {
return EthernetTypeMetadata[a].DecodeWith.Decode(data, p)
}
// String returns EthernetTypeMetadata.Name.
func (a EthernetType) String() string {
return EthernetTypeMetadata[a].Name
}
// LayerType returns EthernetTypeMetadata.LayerType.
func (a EthernetType) LayerType() gopacket.LayerType {
return EthernetTypeMetadata[a].LayerType
}
type errorDecoderForEthernetType int
func (a *errorDecoderForEthernetType) Decode(data []byte, p gopacket.PacketBuilder) error {
return a
}
func (a *errorDecoderForEthernetType) Error() string {
return fmt.Sprintf("Unable to decode EthernetType %d", int(*a))
}
var errorDecodersForEthernetType [65536]errorDecoderForEthernetType
var EthernetTypeMetadata [65536]EnumMetadata
func initUnknownTypesForEthernetType() {
for i := 0; i < 65536; i++ {
errorDecodersForEthernetType[i] = errorDecoderForEthernetType(i)
EthernetTypeMetadata[i] = EnumMetadata{
DecodeWith: &errorDecodersForEthernetType[i],
Name: "UnknownEthernetType",
}
}
}
// Decoder calls PPPTypeMetadata.DecodeWith's decoder.
func (a PPPType) Decode(data []byte, p gopacket.PacketBuilder) error {
return PPPTypeMetadata[a].DecodeWith.Decode(data, p)
}
// String returns PPPTypeMetadata.Name.
func (a PPPType) String() string {
return PPPTypeMetadata[a].Name
}
// LayerType returns PPPTypeMetadata.LayerType.
func (a PPPType) LayerType() gopacket.LayerType {
return PPPTypeMetadata[a].LayerType
}
type errorDecoderForPPPType int
func (a *errorDecoderForPPPType) Decode(data []byte, p gopacket.PacketBuilder) error {
return a
}
func (a *errorDecoderForPPPType) Error() string {
return fmt.Sprintf("Unable to decode PPPType %d", int(*a))
}
var errorDecodersForPPPType [65536]errorDecoderForPPPType
var PPPTypeMetadata [65536]EnumMetadata
func initUnknownTypesForPPPType() {
for i := 0; i < 65536; i++ {
errorDecodersForPPPType[i] = errorDecoderForPPPType(i)
PPPTypeMetadata[i] = EnumMetadata{
DecodeWith: &errorDecodersForPPPType[i],
Name: "UnknownPPPType",
}
}
}
// Decoder calls IPProtocolMetadata.DecodeWith's decoder.
func (a IPProtocol) Decode(data []byte, p gopacket.PacketBuilder) error {
return IPProtocolMetadata[a].DecodeWith.Decode(data, p)
}
// String returns IPProtocolMetadata.Name.
func (a IPProtocol) String() string {
return IPProtocolMetadata[a].Name
}
// LayerType returns IPProtocolMetadata.LayerType.
func (a IPProtocol) LayerType() gopacket.LayerType {
return IPProtocolMetadata[a].LayerType
}
type errorDecoderForIPProtocol int
func (a *errorDecoderForIPProtocol) Decode(data []byte, p gopacket.PacketBuilder) error {
return a
}
func (a *errorDecoderForIPProtocol) Error() string {
return fmt.Sprintf("Unable to decode IPProtocol %d", int(*a))
}
var errorDecodersForIPProtocol [256]errorDecoderForIPProtocol
var IPProtocolMetadata [256]EnumMetadata
func initUnknownTypesForIPProtocol() {
for i := 0; i < 256; i++ {
errorDecodersForIPProtocol[i] = errorDecoderForIPProtocol(i)
IPProtocolMetadata[i] = EnumMetadata{
DecodeWith: &errorDecodersForIPProtocol[i],
Name: "UnknownIPProtocol",
}
}
}
// Decoder calls SCTPChunkTypeMetadata.DecodeWith's decoder.
func (a SCTPChunkType) Decode(data []byte, p gopacket.PacketBuilder) error {
return SCTPChunkTypeMetadata[a].DecodeWith.Decode(data, p)
}
// String returns SCTPChunkTypeMetadata.Name.
func (a SCTPChunkType) String() string {
return SCTPChunkTypeMetadata[a].Name
}
// LayerType returns SCTPChunkTypeMetadata.LayerType.
func (a SCTPChunkType) LayerType() gopacket.LayerType {
return SCTPChunkTypeMetadata[a].LayerType
}
type errorDecoderForSCTPChunkType int
func (a *errorDecoderForSCTPChunkType) Decode(data []byte, p gopacket.PacketBuilder) error {
return a
}
func (a *errorDecoderForSCTPChunkType) Error() string {
return fmt.Sprintf("Unable to decode SCTPChunkType %d", int(*a))
}
var errorDecodersForSCTPChunkType [256]errorDecoderForSCTPChunkType
var SCTPChunkTypeMetadata [256]EnumMetadata
func initUnknownTypesForSCTPChunkType() {
for i := 0; i < 256; i++ {
errorDecodersForSCTPChunkType[i] = errorDecoderForSCTPChunkType(i)
SCTPChunkTypeMetadata[i] = EnumMetadata{
DecodeWith: &errorDecodersForSCTPChunkType[i],
Name: "UnknownSCTPChunkType",
}
}
}
// Decoder calls PPPoECodeMetadata.DecodeWith's decoder.
func (a PPPoECode) Decode(data []byte, p gopacket.PacketBuilder) error {
return PPPoECodeMetadata[a].DecodeWith.Decode(data, p)
}
// String returns PPPoECodeMetadata.Name.
func (a PPPoECode) String() string {
return PPPoECodeMetadata[a].Name
}
// LayerType returns PPPoECodeMetadata.LayerType.
func (a PPPoECode) LayerType() gopacket.LayerType {
return PPPoECodeMetadata[a].LayerType
}
type errorDecoderForPPPoECode int
func (a *errorDecoderForPPPoECode) Decode(data []byte, p gopacket.PacketBuilder) error {
return a
}
func (a *errorDecoderForPPPoECode) Error() string {
return fmt.Sprintf("Unable to decode PPPoECode %d", int(*a))
}
var errorDecodersForPPPoECode [256]errorDecoderForPPPoECode
var PPPoECodeMetadata [256]EnumMetadata
func initUnknownTypesForPPPoECode() {
for i := 0; i < 256; i++ {
errorDecodersForPPPoECode[i] = errorDecoderForPPPoECode(i)
PPPoECodeMetadata[i] = EnumMetadata{
DecodeWith: &errorDecodersForPPPoECode[i],
Name: "UnknownPPPoECode",
}
}
}
// Decoder calls FDDIFrameControlMetadata.DecodeWith's decoder.
func (a FDDIFrameControl) Decode(data []byte, p gopacket.PacketBuilder) error {
return FDDIFrameControlMetadata[a].DecodeWith.Decode(data, p)
}
// String returns FDDIFrameControlMetadata.Name.
func (a FDDIFrameControl) String() string {
return FDDIFrameControlMetadata[a].Name
}
// LayerType returns FDDIFrameControlMetadata.LayerType.
func (a FDDIFrameControl) LayerType() gopacket.LayerType {
return FDDIFrameControlMetadata[a].LayerType
}
type errorDecoderForFDDIFrameControl int
func (a *errorDecoderForFDDIFrameControl) Decode(data []byte, p gopacket.PacketBuilder) error {
return a
}
func (a *errorDecoderForFDDIFrameControl) Error() string {
return fmt.Sprintf("Unable to decode FDDIFrameControl %d", int(*a))
}
var errorDecodersForFDDIFrameControl [256]errorDecoderForFDDIFrameControl
var FDDIFrameControlMetadata [256]EnumMetadata
func initUnknownTypesForFDDIFrameControl() {
for i := 0; i < 256; i++ {
errorDecodersForFDDIFrameControl[i] = errorDecoderForFDDIFrameControl(i)
FDDIFrameControlMetadata[i] = EnumMetadata{
DecodeWith: &errorDecodersForFDDIFrameControl[i],
Name: "UnknownFDDIFrameControl",
}
}
}
// Decoder calls EAPOLTypeMetadata.DecodeWith's decoder.
func (a EAPOLType) Decode(data []byte, p gopacket.PacketBuilder) error {
return EAPOLTypeMetadata[a].DecodeWith.Decode(data, p)
}
// String returns EAPOLTypeMetadata.Name.
func (a EAPOLType) String() string {
return EAPOLTypeMetadata[a].Name
}
// LayerType returns EAPOLTypeMetadata.LayerType.
func (a EAPOLType) LayerType() gopacket.LayerType {
return EAPOLTypeMetadata[a].LayerType
}
type errorDecoderForEAPOLType int
func (a *errorDecoderForEAPOLType) Decode(data []byte, p gopacket.PacketBuilder) error {
return a
}
func (a *errorDecoderForEAPOLType) Error() string {
return fmt.Sprintf("Unable to decode EAPOLType %d", int(*a))
}
var errorDecodersForEAPOLType [256]errorDecoderForEAPOLType
var EAPOLTypeMetadata [256]EnumMetadata
func initUnknownTypesForEAPOLType() {
for i := 0; i < 256; i++ {
errorDecodersForEAPOLType[i] = errorDecoderForEAPOLType(i)
EAPOLTypeMetadata[i] = EnumMetadata{
DecodeWith: &errorDecodersForEAPOLType[i],
Name: "UnknownEAPOLType",
}
}
}
// Decoder calls ProtocolFamilyMetadata.DecodeWith's decoder.
func (a ProtocolFamily) Decode(data []byte, p gopacket.PacketBuilder) error {
return ProtocolFamilyMetadata[a].DecodeWith.Decode(data, p)
}
// String returns ProtocolFamilyMetadata.Name.
func (a ProtocolFamily) String() string {
return ProtocolFamilyMetadata[a].Name
}
// LayerType returns ProtocolFamilyMetadata.LayerType.
func (a ProtocolFamily) LayerType() gopacket.LayerType {
return ProtocolFamilyMetadata[a].LayerType
}
type errorDecoderForProtocolFamily int
func (a *errorDecoderForProtocolFamily) Decode(data []byte, p gopacket.PacketBuilder) error {
return a
}
func (a *errorDecoderForProtocolFamily) Error() string {
return fmt.Sprintf("Unable to decode ProtocolFamily %d", int(*a))
}
var errorDecodersForProtocolFamily [256]errorDecoderForProtocolFamily
var ProtocolFamilyMetadata [256]EnumMetadata
func initUnknownTypesForProtocolFamily() {
for i := 0; i < 256; i++ {
errorDecodersForProtocolFamily[i] = errorDecoderForProtocolFamily(i)
ProtocolFamilyMetadata[i] = EnumMetadata{
DecodeWith: &errorDecodersForProtocolFamily[i],
Name: "UnknownProtocolFamily",
}
}
}
// Decoder calls Dot11TypeMetadata.DecodeWith's decoder.
func (a Dot11Type) Decode(data []byte, p gopacket.PacketBuilder) error {
return Dot11TypeMetadata[a].DecodeWith.Decode(data, p)
}
// String returns Dot11TypeMetadata.Name.
func (a Dot11Type) String() string {
return Dot11TypeMetadata[a].Name
}
// LayerType returns Dot11TypeMetadata.LayerType.
func (a Dot11Type) LayerType() gopacket.LayerType {
return Dot11TypeMetadata[a].LayerType
}
type errorDecoderForDot11Type int
func (a *errorDecoderForDot11Type) Decode(data []byte, p gopacket.PacketBuilder) error {
return a
}
func (a *errorDecoderForDot11Type) Error() string {
return fmt.Sprintf("Unable to decode Dot11Type %d", int(*a))
}
var errorDecodersForDot11Type [256]errorDecoderForDot11Type
var Dot11TypeMetadata [256]EnumMetadata
func initUnknownTypesForDot11Type() {
for i := 0; i < 256; i++ {
errorDecodersForDot11Type[i] = errorDecoderForDot11Type(i)
Dot11TypeMetadata[i] = EnumMetadata{
DecodeWith: &errorDecodersForDot11Type[i],
Name: "UnknownDot11Type",
}
}
}
// Decoder calls USBTransportTypeMetadata.DecodeWith's decoder.
func (a USBTransportType) Decode(data []byte, p gopacket.PacketBuilder) error {
return USBTransportTypeMetadata[a].DecodeWith.Decode(data, p)
}
// String returns USBTransportTypeMetadata.Name.
func (a USBTransportType) String() string {
return USBTransportTypeMetadata[a].Name
}
// LayerType returns USBTransportTypeMetadata.LayerType.
func (a USBTransportType) LayerType() gopacket.LayerType {
return USBTransportTypeMetadata[a].LayerType
}
type errorDecoderForUSBTransportType int
func (a *errorDecoderForUSBTransportType) Decode(data []byte, p gopacket.PacketBuilder) error {
return a
}
func (a *errorDecoderForUSBTransportType) Error() string {
return fmt.Sprintf("Unable to decode USBTransportType %d", int(*a))
}
var errorDecodersForUSBTransportType [256]errorDecoderForUSBTransportType
var USBTransportTypeMetadata [256]EnumMetadata
func initUnknownTypesForUSBTransportType() {
for i := 0; i < 256; i++ {
errorDecodersForUSBTransportType[i] = errorDecoderForUSBTransportType(i)
USBTransportTypeMetadata[i] = EnumMetadata{
DecodeWith: &errorDecodersForUSBTransportType[i],
Name: "UnknownUSBTransportType",
}
}
}
+86
View File
@@ -0,0 +1,86 @@
// Copyright 2018 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"
)
const (
//ERSPANIIVersionObsolete - The obsolete value for the version field
ERSPANIIVersionObsolete = 0x0
// ERSPANIIVersion - The current value for the version field
ERSPANIIVersion = 0x1
)
// ERSPANII contains all of the fields found in an ERSPAN Type II header
// https://tools.ietf.org/html/draft-foschiano-erspan-03
type ERSPANII struct {
BaseLayer
IsTruncated bool
Version, CoS, TrunkEncap uint8
VLANIdentifier, SessionID, Reserved uint16
Index uint32
}
func (erspan2 *ERSPANII) LayerType() gopacket.LayerType { return LayerTypeERSPANII }
// DecodeFromBytes decodes the given bytes into this layer.
func (erspan2 *ERSPANII) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
erspan2Length := 8
erspan2.Version = data[0] & 0xF0 >> 4
erspan2.VLANIdentifier = binary.BigEndian.Uint16(data[:2]) & 0x0FFF
erspan2.CoS = data[2] & 0xE0 >> 5
erspan2.TrunkEncap = data[2] & 0x18 >> 3
erspan2.IsTruncated = data[2]&0x4>>2 != 0
erspan2.SessionID = binary.BigEndian.Uint16(data[2:4]) & 0x03FF
erspan2.Reserved = binary.BigEndian.Uint16(data[4:6]) & 0xFFF0 >> 4
erspan2.Index = binary.BigEndian.Uint32(data[4:8]) & 0x000FFFFF
erspan2.Contents = data[:erspan2Length]
erspan2.Payload = data[erspan2Length:]
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 (erspan2 *ERSPANII) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
bytes, err := b.PrependBytes(8)
if err != nil {
return err
}
twoByteInt := uint16(erspan2.Version&0xF)<<12 | erspan2.VLANIdentifier&0x0FFF
binary.BigEndian.PutUint16(bytes, twoByteInt)
twoByteInt = uint16(erspan2.CoS&0x7)<<13 | uint16(erspan2.TrunkEncap&0x3)<<11 | erspan2.SessionID&0x03FF
if erspan2.IsTruncated {
twoByteInt |= 0x400
}
binary.BigEndian.PutUint16(bytes[2:], twoByteInt)
fourByteInt := uint32(erspan2.Reserved&0x0FFF)<<20 | erspan2.Index&0x000FFFFF
binary.BigEndian.PutUint32(bytes[4:], fourByteInt)
return nil
}
// CanDecode returns the set of layer types that this DecodingLayer can decode.
func (erspan2 *ERSPANII) CanDecode() gopacket.LayerClass {
return LayerTypeERSPANII
}
// NextLayerType returns the layer type contained by this DecodingLayer.
func (erspan2 *ERSPANII) NextLayerType() gopacket.LayerType {
return LayerTypeEthernet
}
func decodeERSPANII(data []byte, p gopacket.PacketBuilder) error {
erspan2 := &ERSPANII{}
return decodingLayerDecoder(erspan2, data, p)
}
+45
View File
@@ -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)
}
+123
View File
@@ -0,0 +1,123 @@
// 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:]}
eth.Length = 0
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)
}
+41
View File
@@ -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)
}
+39
View File
@@ -0,0 +1,39 @@
// Copyright 2019 The GoPacket Authors. All rights reserved.
//
// Use of this source code is governed by a BSD-style license that can be found
// in the LICENSE file in the root of the source tree.
package layers
import (
"encoding/binary"
"github.com/google/gopacket"
)
// FuzzLayer is a fuzz target for the layers package of gopacket
// A fuzz target is a function processing a binary blob (byte slice)
// The process here is to interpret this data as a packet, and print the layers contents.
// The decoding options and the starting layer are encoded in the first bytes.
// The function returns 1 if this is a valid packet (no error layer)
func FuzzLayer(data []byte) int {
if len(data) < 3 {
return 0
}
// use the first two bytes to choose the top level layer
startLayer := binary.BigEndian.Uint16(data[:2])
var fuzzOpts = gopacket.DecodeOptions{
Lazy: data[2]&0x1 != 0,
NoCopy: data[2]&0x2 != 0,
SkipDecodeRecovery: data[2]&0x4 != 0,
DecodeStreamsAsDatagrams: data[2]&0x8 != 0,
}
p := gopacket.NewPacket(data[3:], gopacket.LayerType(startLayer), fuzzOpts)
for _, l := range p.Layers() {
gopacket.LayerString(l)
}
if p.ErrorLayer() != nil {
return 0
}
return 1
}
+3
View File
@@ -0,0 +1,3 @@
#!/bin/bash
for i in *.go; do golint $i | grep -q . || echo $i; done > .linted
+121
View File
@@ -0,0 +1,121 @@
// 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"
"errors"
"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, df gopacket.DecodeFeedback) (*GeneveOption, uint8, error) {
if len(data) < 3 {
df.SetTruncated()
return nil, 0, errors.New("geneve option too small")
}
opt := &GeneveOption{}
opt.Class = binary.BigEndian.Uint16(data[0:2])
opt.Type = data[2]
opt.Flags = data[3] >> 4
opt.Length = (data[3]&0xf)*4 + 4
if len(data) < int(opt.Length) {
df.SetTruncated()
return nil, 0, errors.New("geneve option too small")
}
opt.Data = make([]byte, opt.Length-4)
copy(opt.Data, data[4:opt.Length])
return opt, opt.Length, nil
}
func (gn *Geneve) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
if len(data) < 7 {
df.SetTruncated()
return errors.New("geneve packet too short")
}
gn.Version = data[0] >> 7
gn.OptionsLength = (data[0] & 0x3f) * 4
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), int32(gn.OptionsLength)
if len(data) < int(length+7) {
df.SetTruncated()
return errors.New("geneve packet too short")
}
for length > 0 {
opt, len, err := decodeGeneveOption(data[offset:], gn, df)
if err != nil {
return err
}
gn.Options = append(gn.Options, opt)
length -= int32(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)
}
+200
View File
@@ -0,0 +1,200 @@
// 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, AckPresent bool
RecursionControl, Flags, Version uint8
Protocol EthernetType
Checksum, Offset uint16
Key, Seq, Ack 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.AckPresent = data[1]&0x80 != 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
}
}
if g.AckPresent {
g.Ack = binary.BigEndian.Uint32(data[offset : offset+4])
offset += 4
}
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
}
if g.AckPresent {
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
}
if g.AckPresent {
buf[1] |= 0x80
}
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.AckPresent {
binary.BigEndian.PutUint32(buf[offset:offset+4], g.Ack)
offset += 4
}
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)
}
+184
View File
@@ -0,0 +1,184 @@
// Copyright 2017 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"
)
const gtpMinimumSizeInBytes int = 8
// GTPExtensionHeader is used to carry extra data and enable future extensions of the GTP without the need to use another version number.
type GTPExtensionHeader struct {
Type uint8
Content []byte
}
// GTPv1U protocol is used to exchange user data over GTP tunnels across the Sx interfaces.
// Defined in https://portal.3gpp.org/desktopmodules/Specifications/SpecificationDetails.aspx?specificationId=1595
type GTPv1U struct {
BaseLayer
Version uint8
ProtocolType uint8
Reserved uint8
ExtensionHeaderFlag bool
SequenceNumberFlag bool
NPDUFlag bool
MessageType uint8
MessageLength uint16
TEID uint32
SequenceNumber uint16
NPDU uint8
GTPExtensionHeaders []GTPExtensionHeader
}
// LayerType returns LayerTypeGTPV1U
func (g *GTPv1U) LayerType() gopacket.LayerType { return LayerTypeGTPv1U }
// DecodeFromBytes analyses a byte slice and attempts to decode it as a GTPv1U packet
func (g *GTPv1U) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
hLen := gtpMinimumSizeInBytes
dLen := len(data)
if dLen < hLen {
return fmt.Errorf("GTP packet too small: %d bytes", dLen)
}
g.Version = (data[0] >> 5) & 0x07
g.ProtocolType = (data[0] >> 4) & 0x01
g.Reserved = (data[0] >> 3) & 0x01
g.SequenceNumberFlag = ((data[0] >> 1) & 0x01) == 1
g.NPDUFlag = (data[0] & 0x01) == 1
g.ExtensionHeaderFlag = ((data[0] >> 2) & 0x01) == 1
g.MessageType = data[1]
g.MessageLength = binary.BigEndian.Uint16(data[2:4])
pLen := 8 + g.MessageLength
if uint16(dLen) < pLen {
return fmt.Errorf("GTP packet too small: %d bytes", dLen)
}
// Field used to multiplex different connections in the same GTP tunnel.
g.TEID = binary.BigEndian.Uint32(data[4:8])
cIndex := uint16(hLen)
if g.SequenceNumberFlag || g.NPDUFlag || g.ExtensionHeaderFlag {
hLen += 4
cIndex += 4
if dLen < hLen {
return fmt.Errorf("GTP packet too small: %d bytes", dLen)
}
if g.SequenceNumberFlag {
g.SequenceNumber = binary.BigEndian.Uint16(data[8:10])
}
if g.NPDUFlag {
g.NPDU = data[10]
}
if g.ExtensionHeaderFlag {
extensionFlag := true
for extensionFlag {
extensionType := uint8(data[cIndex-1])
extensionLength := uint(data[cIndex])
if extensionLength == 0 {
return fmt.Errorf("GTP packet with invalid extension header")
}
// extensionLength is in 4-octet units
lIndex := cIndex + (uint16(extensionLength) * 4)
if uint16(dLen) < lIndex {
fmt.Println(dLen, lIndex)
return fmt.Errorf("GTP packet with small extension header: %d bytes", dLen)
}
content := data[cIndex+1 : lIndex-1]
eh := GTPExtensionHeader{Type: extensionType, Content: content}
g.GTPExtensionHeaders = append(g.GTPExtensionHeaders, eh)
cIndex = lIndex
// Check if coming bytes are from an extension header
extensionFlag = data[cIndex-1] != 0
}
}
}
g.BaseLayer = BaseLayer{Contents: data[:cIndex], Payload: data[cIndex:]}
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 *GTPv1U) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
data, err := b.PrependBytes(gtpMinimumSizeInBytes)
if err != nil {
return err
}
data[0] |= (g.Version << 5)
data[0] |= (1 << 4)
if len(g.GTPExtensionHeaders) > 0 {
data[0] |= 0x04
g.ExtensionHeaderFlag = true
}
if g.SequenceNumberFlag {
data[0] |= 0x02
}
if g.NPDUFlag {
data[0] |= 0x01
}
data[1] = g.MessageType
binary.BigEndian.PutUint16(data[2:4], g.MessageLength)
binary.BigEndian.PutUint32(data[4:8], g.TEID)
if g.ExtensionHeaderFlag || g.SequenceNumberFlag || g.NPDUFlag {
data, err := b.AppendBytes(4)
if err != nil {
return err
}
binary.BigEndian.PutUint16(data[:2], g.SequenceNumber)
data[2] = g.NPDU
for _, eh := range g.GTPExtensionHeaders {
data[len(data)-1] = eh.Type
lContent := len(eh.Content)
// extensionLength is in 4-octet units
extensionLength := (lContent + 2) / 4
// Get two extra byte for the next extension header type and length
data, err = b.AppendBytes(lContent + 2)
if err != nil {
return err
}
data[0] = byte(extensionLength)
copy(data[1:lContent+1], eh.Content)
}
}
return nil
}
// CanDecode returns a set of layers that GTP objects can decode.
func (g *GTPv1U) CanDecode() gopacket.LayerClass {
return LayerTypeGTPv1U
}
// NextLayerType specifies the next layer that GoPacket should attempt to
func (g *GTPv1U) NextLayerType() gopacket.LayerType {
if len(g.LayerPayload()) == 0 {
return gopacket.LayerTypeZero
}
version := uint8(g.LayerPayload()[0]) >> 4
if version == 4 {
return LayerTypeIPv4
} else if version == 6 {
return LayerTypeIPv6
} else {
return LayerTypePPP
}
}
func decodeGTPv1u(data []byte, p gopacket.PacketBuilder) error {
gtp := &GTPv1U{}
err := gtp.DecodeFromBytes(data, p)
if err != nil {
return err
}
p.AddLayer(gtp)
return p.NextDecoder(gtp.NextLayerType())
}
File diff suppressed because it is too large Load Diff
+267
View File
@@ -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)
}
+266
View File
@@ -0,0 +1,266 @@
// 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
// The following are from RFC 2710
ICMPv6TypeMLDv1MulticastListenerQueryMessage = 130
ICMPv6TypeMLDv1MulticastListenerReportMessage = 131
ICMPv6TypeMLDv1MulticastListenerDoneMessage = 132
// The following are from RFC 3810
ICMPv6TypeMLDv2MulticastListenerReportMessageV2 = 143
)
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 is deprecated and always nil. See the different ICMPv6 message types
// instead (e.g. ICMPv6TypeRouterSolicitation).
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) < 4 {
df.SetTruncated()
return errors.New("ICMP layer less then 4 bytes for ICMPv6 packet")
}
i.TypeCode = CreateICMPv6TypeCode(data[0], data[1])
i.Checksum = binary.BigEndian.Uint16(data[2:4])
i.BaseLayer = BaseLayer{data[:4], data[4:]}
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 {
bytes, err := b.PrependBytes(4)
if err != nil {
return err
}
i.TypeCode.SerializeTo(bytes)
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 {
switch i.TypeCode.Type() {
case ICMPv6TypeEchoRequest:
return LayerTypeICMPv6Echo
case ICMPv6TypeEchoReply:
return LayerTypeICMPv6Echo
case ICMPv6TypeRouterSolicitation:
return LayerTypeICMPv6RouterSolicitation
case ICMPv6TypeRouterAdvertisement:
return LayerTypeICMPv6RouterAdvertisement
case ICMPv6TypeNeighborSolicitation:
return LayerTypeICMPv6NeighborSolicitation
case ICMPv6TypeNeighborAdvertisement:
return LayerTypeICMPv6NeighborAdvertisement
case ICMPv6TypeRedirect:
return LayerTypeICMPv6Redirect
case ICMPv6TypeMLDv1MulticastListenerQueryMessage: // Same Code for MLDv1 Query and MLDv2 Query
if len(i.Payload) > 20 { // Only payload size differs
return LayerTypeMLDv2MulticastListenerQuery
} else {
return LayerTypeMLDv1MulticastListenerQuery
}
case ICMPv6TypeMLDv1MulticastListenerDoneMessage:
return LayerTypeMLDv1MulticastListenerDone
case ICMPv6TypeMLDv1MulticastListenerReportMessage:
return LayerTypeMLDv1MulticastListenerReport
case ICMPv6TypeMLDv2MulticastListenerReportMessageV2:
return LayerTypeMLDv2MulticastListenerReport
}
return gopacket.LayerTypePayload
}
func decodeICMPv6(data []byte, p gopacket.PacketBuilder) error {
i := &ICMPv6{}
return decodingLayerDecoder(i, data, p)
}
+578
View File
@@ -0,0 +1,578 @@
// 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"
"encoding/hex"
"errors"
"fmt"
"net"
"time"
"github.com/google/gopacket"
)
// Based on RFC 4861
// ICMPv6Opt indicate how to decode the data associated with each ICMPv6Option.
type ICMPv6Opt uint8
const (
_ ICMPv6Opt = iota
// ICMPv6OptSourceAddress contains the link-layer address of the sender of
// the packet. It is used in the Neighbor Solicitation, Router
// Solicitation, and Router Advertisement packets. Must be ignored for other
// Neighbor discovery messages.
ICMPv6OptSourceAddress
// ICMPv6OptTargetAddress contains the link-layer address of the target. It
// is used in Neighbor Advertisement and Redirect packets. Must be ignored
// for other Neighbor discovery messages.
ICMPv6OptTargetAddress
// ICMPv6OptPrefixInfo provides hosts with on-link prefixes and prefixes
// for Address Autoconfiguration. The Prefix Information option appears in
// Router Advertisement packets and MUST be silently ignored for other
// messages.
ICMPv6OptPrefixInfo
// ICMPv6OptRedirectedHeader is used in Redirect messages and contains all
// or part of the packet that is being redirected.
ICMPv6OptRedirectedHeader
// ICMPv6OptMTU is used in Router Advertisement messages to ensure that all
// nodes on a link use the same MTU value in those cases where the link MTU
// is not well known. This option MUST be silently ignored for other
// Neighbor Discovery messages.
ICMPv6OptMTU
)
// ICMPv6Echo represents the structure of a ping.
type ICMPv6Echo struct {
BaseLayer
Identifier uint16
SeqNumber uint16
}
// ICMPv6RouterSolicitation is sent by hosts to find routers.
type ICMPv6RouterSolicitation struct {
BaseLayer
Options ICMPv6Options
}
// ICMPv6RouterAdvertisement is sent by routers in response to Solicitation.
type ICMPv6RouterAdvertisement struct {
BaseLayer
HopLimit uint8
Flags uint8
RouterLifetime uint16
ReachableTime uint32
RetransTimer uint32
Options ICMPv6Options
}
// ICMPv6NeighborSolicitation is sent to request the link-layer address of a
// target node.
type ICMPv6NeighborSolicitation struct {
BaseLayer
TargetAddress net.IP
Options ICMPv6Options
}
// ICMPv6NeighborAdvertisement is sent by nodes in response to Solicitation.
type ICMPv6NeighborAdvertisement struct {
BaseLayer
Flags uint8
TargetAddress net.IP
Options ICMPv6Options
}
// ICMPv6Redirect is sent by routers to inform hosts of a better first-hop node
// on the path to a destination.
type ICMPv6Redirect struct {
BaseLayer
TargetAddress net.IP
DestinationAddress net.IP
Options ICMPv6Options
}
// ICMPv6Option contains the type and data for a single option.
type ICMPv6Option struct {
Type ICMPv6Opt
Data []byte
}
// ICMPv6Options is a slice of ICMPv6Option.
type ICMPv6Options []ICMPv6Option
func (i ICMPv6Opt) String() string {
switch i {
case ICMPv6OptSourceAddress:
return "SourceAddress"
case ICMPv6OptTargetAddress:
return "TargetAddress"
case ICMPv6OptPrefixInfo:
return "PrefixInfo"
case ICMPv6OptRedirectedHeader:
return "RedirectedHeader"
case ICMPv6OptMTU:
return "MTU"
default:
return fmt.Sprintf("Unknown(%d)", i)
}
}
// CanDecode returns the set of layer types that this DecodingLayer can decode.
func (i *ICMPv6Echo) CanDecode() gopacket.LayerClass {
return LayerTypeICMPv6Echo
}
// LayerType returns LayerTypeICMPv6Echo.
func (i *ICMPv6Echo) LayerType() gopacket.LayerType {
return LayerTypeICMPv6Echo
}
// NextLayerType returns the layer type contained by this DecodingLayer.
func (i *ICMPv6Echo) NextLayerType() gopacket.LayerType {
return gopacket.LayerTypePayload
}
// DecodeFromBytes decodes the given bytes into this layer.
func (i *ICMPv6Echo) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
if len(data) < 4 {
df.SetTruncated()
return errors.New("ICMP layer less then 4 bytes for ICMPv6 Echo")
}
i.Identifier = binary.BigEndian.Uint16(data[0:2])
i.SeqNumber = binary.BigEndian.Uint16(data[2:4])
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 *ICMPv6Echo) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
buf, err := b.PrependBytes(4)
if err != nil {
return err
}
binary.BigEndian.PutUint16(buf, i.Identifier)
binary.BigEndian.PutUint16(buf[2:], i.SeqNumber)
return nil
}
// LayerType returns LayerTypeICMPv6.
func (i *ICMPv6RouterSolicitation) LayerType() gopacket.LayerType {
return LayerTypeICMPv6RouterSolicitation
}
// NextLayerType returns the layer type contained by this DecodingLayer.
func (i *ICMPv6RouterSolicitation) NextLayerType() gopacket.LayerType {
return gopacket.LayerTypePayload
}
// DecodeFromBytes decodes the given bytes into this layer.
func (i *ICMPv6RouterSolicitation) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
// first 4 bytes are reserved followed by options
if len(data) < 4 {
df.SetTruncated()
return errors.New("ICMP layer less then 4 bytes for ICMPv6 router solicitation")
}
// truncate old options
i.Options = i.Options[:0]
return i.Options.DecodeFromBytes(data[4:], df)
}
// 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 *ICMPv6RouterSolicitation) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
if err := i.Options.SerializeTo(b, opts); err != nil {
return err
}
buf, err := b.PrependBytes(4)
if err != nil {
return err
}
copy(buf, lotsOfZeros[:4])
return nil
}
// CanDecode returns the set of layer types that this DecodingLayer can decode.
func (i *ICMPv6RouterSolicitation) CanDecode() gopacket.LayerClass {
return LayerTypeICMPv6RouterSolicitation
}
// LayerType returns LayerTypeICMPv6RouterAdvertisement.
func (i *ICMPv6RouterAdvertisement) LayerType() gopacket.LayerType {
return LayerTypeICMPv6RouterAdvertisement
}
// NextLayerType returns the layer type contained by this DecodingLayer.
func (i *ICMPv6RouterAdvertisement) NextLayerType() gopacket.LayerType {
return gopacket.LayerTypePayload
}
// DecodeFromBytes decodes the given bytes into this layer.
func (i *ICMPv6RouterAdvertisement) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
if len(data) < 12 {
df.SetTruncated()
return errors.New("ICMP layer less then 12 bytes for ICMPv6 router advertisement")
}
i.HopLimit = uint8(data[0])
// M, O bit followed by 6 reserved bits
i.Flags = uint8(data[1])
i.RouterLifetime = binary.BigEndian.Uint16(data[2:4])
i.ReachableTime = binary.BigEndian.Uint32(data[4:8])
i.RetransTimer = binary.BigEndian.Uint32(data[8:12])
i.BaseLayer = BaseLayer{data, nil} // assume no payload
// truncate old options
i.Options = i.Options[:0]
return i.Options.DecodeFromBytes(data[12:], df)
}
// 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 *ICMPv6RouterAdvertisement) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
if err := i.Options.SerializeTo(b, opts); err != nil {
return err
}
buf, err := b.PrependBytes(12)
if err != nil {
return err
}
buf[0] = byte(i.HopLimit)
buf[1] = byte(i.Flags)
binary.BigEndian.PutUint16(buf[2:], i.RouterLifetime)
binary.BigEndian.PutUint32(buf[4:], i.ReachableTime)
binary.BigEndian.PutUint32(buf[8:], i.RetransTimer)
return nil
}
// CanDecode returns the set of layer types that this DecodingLayer can decode.
func (i *ICMPv6RouterAdvertisement) CanDecode() gopacket.LayerClass {
return LayerTypeICMPv6RouterAdvertisement
}
// ManagedAddressConfig is true when addresses are available via DHCPv6. If
// set, the OtherConfig flag is redundant.
func (i *ICMPv6RouterAdvertisement) ManagedAddressConfig() bool {
return i.Flags&0x80 != 0
}
// OtherConfig is true when there is other configuration information available
// via DHCPv6. For example, DNS-related information.
func (i *ICMPv6RouterAdvertisement) OtherConfig() bool {
return i.Flags&0x40 != 0
}
// LayerType returns LayerTypeICMPv6NeighborSolicitation.
func (i *ICMPv6NeighborSolicitation) LayerType() gopacket.LayerType {
return LayerTypeICMPv6NeighborSolicitation
}
// NextLayerType returns the layer type contained by this DecodingLayer.
func (i *ICMPv6NeighborSolicitation) NextLayerType() gopacket.LayerType {
return gopacket.LayerTypePayload
}
// DecodeFromBytes decodes the given bytes into this layer.
func (i *ICMPv6NeighborSolicitation) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
if len(data) < 20 {
df.SetTruncated()
return errors.New("ICMP layer less then 20 bytes for ICMPv6 neighbor solicitation")
}
i.TargetAddress = net.IP(data[4:20])
i.BaseLayer = BaseLayer{data, nil} // assume no payload
// truncate old options
i.Options = i.Options[:0]
return i.Options.DecodeFromBytes(data[20:], df)
}
// 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 *ICMPv6NeighborSolicitation) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
if err := i.Options.SerializeTo(b, opts); err != nil {
return err
}
buf, err := b.PrependBytes(20)
if err != nil {
return err
}
copy(buf, lotsOfZeros[:4])
copy(buf[4:], i.TargetAddress)
return nil
}
// CanDecode returns the set of layer types that this DecodingLayer can decode.
func (i *ICMPv6NeighborSolicitation) CanDecode() gopacket.LayerClass {
return LayerTypeICMPv6NeighborSolicitation
}
// LayerType returns LayerTypeICMPv6NeighborAdvertisement.
func (i *ICMPv6NeighborAdvertisement) LayerType() gopacket.LayerType {
return LayerTypeICMPv6NeighborAdvertisement
}
// NextLayerType returns the layer type contained by this DecodingLayer.
func (i *ICMPv6NeighborAdvertisement) NextLayerType() gopacket.LayerType {
return gopacket.LayerTypePayload
}
// DecodeFromBytes decodes the given bytes into this layer.
func (i *ICMPv6NeighborAdvertisement) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
if len(data) < 20 {
df.SetTruncated()
return errors.New("ICMP layer less then 20 bytes for ICMPv6 neighbor advertisement")
}
i.Flags = uint8(data[0])
i.TargetAddress = net.IP(data[4:20])
i.BaseLayer = BaseLayer{data, nil} // assume no payload
// truncate old options
i.Options = i.Options[:0]
return i.Options.DecodeFromBytes(data[20:], df)
}
// 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 *ICMPv6NeighborAdvertisement) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
if err := i.Options.SerializeTo(b, opts); err != nil {
return err
}
buf, err := b.PrependBytes(20)
if err != nil {
return err
}
buf[0] = byte(i.Flags)
copy(buf[1:], lotsOfZeros[:3])
copy(buf[4:], i.TargetAddress)
return nil
}
// CanDecode returns the set of layer types that this DecodingLayer can decode.
func (i *ICMPv6NeighborAdvertisement) CanDecode() gopacket.LayerClass {
return LayerTypeICMPv6NeighborAdvertisement
}
// Router indicates whether the sender is a router or not.
func (i *ICMPv6NeighborAdvertisement) Router() bool {
return i.Flags&0x80 != 0
}
// Solicited indicates whether the advertisement was solicited or not.
func (i *ICMPv6NeighborAdvertisement) Solicited() bool {
return i.Flags&0x40 != 0
}
// Override indicates whether the advertisement should Override an existing
// cache entry.
func (i *ICMPv6NeighborAdvertisement) Override() bool {
return i.Flags&0x20 != 0
}
// LayerType returns LayerTypeICMPv6Redirect.
func (i *ICMPv6Redirect) LayerType() gopacket.LayerType {
return LayerTypeICMPv6Redirect
}
// NextLayerType returns the layer type contained by this DecodingLayer.
func (i *ICMPv6Redirect) NextLayerType() gopacket.LayerType {
return gopacket.LayerTypePayload
}
// DecodeFromBytes decodes the given bytes into this layer.
func (i *ICMPv6Redirect) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
if len(data) < 36 {
df.SetTruncated()
return errors.New("ICMP layer less then 36 bytes for ICMPv6 redirect")
}
i.TargetAddress = net.IP(data[4:20])
i.DestinationAddress = net.IP(data[20:36])
i.BaseLayer = BaseLayer{data, nil} // assume no payload
// truncate old options
i.Options = i.Options[:0]
return i.Options.DecodeFromBytes(data[36:], df)
}
// 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 *ICMPv6Redirect) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
if err := i.Options.SerializeTo(b, opts); err != nil {
return err
}
buf, err := b.PrependBytes(36)
if err != nil {
return err
}
copy(buf, lotsOfZeros[:4])
copy(buf[4:], i.TargetAddress)
copy(buf[20:], i.DestinationAddress)
return nil
}
// CanDecode returns the set of layer types that this DecodingLayer can decode.
func (i *ICMPv6Redirect) CanDecode() gopacket.LayerClass {
return LayerTypeICMPv6Redirect
}
func (i ICMPv6Option) String() string {
hd := hex.EncodeToString(i.Data)
if len(hd) > 0 {
hd = " 0x" + hd
}
switch i.Type {
case ICMPv6OptSourceAddress, ICMPv6OptTargetAddress:
return fmt.Sprintf("ICMPv6Option(%s:%v)",
i.Type,
net.HardwareAddr(i.Data))
case ICMPv6OptPrefixInfo:
if len(i.Data) == 30 {
prefixLen := uint8(i.Data[0])
onLink := (i.Data[1]&0x80 != 0)
autonomous := (i.Data[1]&0x40 != 0)
validLifetime := time.Duration(binary.BigEndian.Uint32(i.Data[2:6])) * time.Second
preferredLifetime := time.Duration(binary.BigEndian.Uint32(i.Data[6:10])) * time.Second
prefix := net.IP(i.Data[14:])
return fmt.Sprintf("ICMPv6Option(%s:%v/%v:%t:%t:%v:%v)",
i.Type,
prefix, prefixLen,
onLink, autonomous,
validLifetime, preferredLifetime)
}
case ICMPv6OptRedirectedHeader:
// could invoke IP decoder on data... probably best not to
break
case ICMPv6OptMTU:
if len(i.Data) == 6 {
return fmt.Sprintf("ICMPv6Option(%s:%v)",
i.Type,
binary.BigEndian.Uint32(i.Data[2:]))
}
}
return fmt.Sprintf("ICMPv6Option(%s:%s)", i.Type, hd)
}
// DecodeFromBytes decodes the given bytes into this layer.
func (i *ICMPv6Options) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
for len(data) > 0 {
if len(data) < 2 {
df.SetTruncated()
return errors.New("ICMP layer less then 2 bytes for ICMPv6 message option")
}
// unit is 8 octets, convert to bytes
length := int(data[1]) * 8
if length == 0 {
df.SetTruncated()
return errors.New("ICMPv6 message option with length 0")
}
if len(data) < length {
df.SetTruncated()
return fmt.Errorf("ICMP layer only %v bytes for ICMPv6 message option with length %v", len(data), length)
}
o := ICMPv6Option{
Type: ICMPv6Opt(data[0]),
Data: data[2:length],
}
// chop off option we just consumed
data = data[length:]
*i = append(*i, o)
}
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 *ICMPv6Options) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
for _, opt := range []ICMPv6Option(*i) {
length := len(opt.Data) + 2
buf, err := b.PrependBytes(length)
if err != nil {
return err
}
buf[0] = byte(opt.Type)
buf[1] = byte(length / 8)
copy(buf[2:], opt.Data)
}
return nil
}
func decodeICMPv6Echo(data []byte, p gopacket.PacketBuilder) error {
i := &ICMPv6Echo{}
return decodingLayerDecoder(i, data, p)
}
func decodeICMPv6RouterSolicitation(data []byte, p gopacket.PacketBuilder) error {
i := &ICMPv6RouterSolicitation{}
return decodingLayerDecoder(i, data, p)
}
func decodeICMPv6RouterAdvertisement(data []byte, p gopacket.PacketBuilder) error {
i := &ICMPv6RouterAdvertisement{}
return decodingLayerDecoder(i, data, p)
}
func decodeICMPv6NeighborSolicitation(data []byte, p gopacket.PacketBuilder) error {
i := &ICMPv6NeighborSolicitation{}
return decodingLayerDecoder(i, data, p)
}
func decodeICMPv6NeighborAdvertisement(data []byte, p gopacket.PacketBuilder) error {
i := &ICMPv6NeighborAdvertisement{}
return decodingLayerDecoder(i, data, p)
}
func decodeICMPv6Redirect(data []byte, p gopacket.PacketBuilder) error {
i := &ICMPv6Redirect{}
return decodingLayerDecoder(i, data, p)
}
+355
View File
@@ -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.")
}
+325
View File
@@ -0,0 +1,325 @@
// 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 {
if len(data) < 20 {
df.SetTruncated()
return fmt.Errorf("Invalid ip4 header. Length %d less than 20", len(data))
}
flagsfrags := binary.BigEndian.Uint16(data[6:8])
ip.Version = uint8(data[0]) >> 4
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]
ip.Padding = nil
// 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:]
return nil
case 1: // 1 byte padding
opt.OptionLength = 1
data = data[1:]
ip.Options = append(ip.Options, opt)
default:
if len(data) < 2 {
df.SetTruncated()
return fmt.Errorf("Invalid ip4 option length. Length %d less than 2", len(data))
}
opt.OptionLength = data[1]
if len(data) < int(opt.OptionLength) {
df.SetTruncated()
return fmt.Errorf("IP option length exceeds remaining IP header size, option type %v length %v", opt.OptionType, opt.OptionLength)
}
if opt.OptionLength <= 2 {
return fmt.Errorf("Invalid IP option type %v length %d. Must be greater than 2", opt.OptionType, opt.OptionLength)
}
opt.OptionData = data[2:opt.OptionLength]
data = data[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
}
+722
View File
@@ -0,0 +1,722 @@
// 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 code as defined in RFC 2675
IPv6HopByHopOptionJumbogram = 0xC2
)
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 (ipv6 *IPv6) LayerType() gopacket.LayerType { return LayerTypeIPv6 }
// NetworkFlow returns this new Flow (EndpointIPv6, SrcIP, DstIP)
func (ipv6 *IPv6) NetworkFlow() gopacket.Flow {
return gopacket.NewFlow(EndpointIPv6, ipv6.SrcIP, ipv6.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++
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 (ipv6 *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(ipv6)
} else if ipv6.HopByHop == nil {
return fmt.Errorf("Cannot fit payload length of %d into IPv6 packet", pLen)
} else {
_, ok, err := getIPv6HopByHopJumboLength(ipv6.HopByHop)
if err != nil {
return err
}
if !ok {
return errors.New("Missing jumbo length hop-by-hop option")
}
}
}
hbhAlreadySerialized := false
if ipv6.HopByHop != nil {
for _, l := range b.Layers() {
if l == LayerTypeIPv6HopByHop {
hbhAlreadySerialized = true
break
}
}
}
if ipv6.HopByHop != nil && !hbhAlreadySerialized {
if ipv6.NextHeader != IPProtocolIPv6HopByHop {
// Just fix it instead of throwing an error
ipv6.NextHeader = IPProtocolIPv6HopByHop
}
err = ipv6.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] = (ipv6.Version << 4) | (ipv6.TrafficClass >> 4)
bytes[1] = (ipv6.TrafficClass << 4) | uint8(ipv6.FlowLabel>>16)
binary.BigEndian.PutUint16(bytes[2:], uint16(ipv6.FlowLabel))
if opts.FixLengths {
if jumbo {
ipv6.Length = 0
} else {
ipv6.Length = uint16(pLen)
}
}
binary.BigEndian.PutUint16(bytes[4:], ipv6.Length)
bytes[6] = byte(ipv6.NextHeader)
bytes[7] = byte(ipv6.HopLimit)
if err := ipv6.AddressTo16(); err != nil {
return err
}
copy(bytes[8:], ipv6.SrcIP)
copy(bytes[24:], ipv6.DstIP)
return nil
}
// DecodeFromBytes implementation according to gopacket.DecodingLayer
func (ipv6 *IPv6) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
if len(data) < 40 {
df.SetTruncated()
return fmt.Errorf("Invalid ip6 header. Length %d less than 40", len(data))
}
ipv6.Version = uint8(data[0]) >> 4
ipv6.TrafficClass = uint8((binary.BigEndian.Uint16(data[0:2]) >> 4) & 0x00FF)
ipv6.FlowLabel = binary.BigEndian.Uint32(data[0:4]) & 0x000FFFFF
ipv6.Length = binary.BigEndian.Uint16(data[4:6])
ipv6.NextHeader = IPProtocol(data[6])
ipv6.HopLimit = data[7]
ipv6.SrcIP = data[8:24]
ipv6.DstIP = data[24:40]
ipv6.HopByHop = nil
ipv6.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 ipv6.NextHeader == IPProtocolIPv6HopByHop {
err := ipv6.hbh.DecodeFromBytes(ipv6.Payload, df)
if err != nil {
return err
}
ipv6.HopByHop = &ipv6.hbh
pEnd, jumbo, err := getIPv6HopByHopJumboLength(ipv6.HopByHop)
if err != nil {
return err
}
if jumbo && ipv6.Length == 0 {
pEnd := int(pEnd)
if pEnd > len(ipv6.Payload) {
df.SetTruncated()
pEnd = len(ipv6.Payload)
}
ipv6.Payload = ipv6.Payload[:pEnd]
return nil
} else if jumbo && ipv6.Length != 0 {
return errors.New("IPv6 has jumbo length and IPv6 length is not 0")
} else if !jumbo && ipv6.Length == 0 {
return errors.New("IPv6 length 0, but HopByHop header does not have jumbogram option")
} else {
ipv6.Payload = ipv6.Payload[ipv6.hbh.ActualLength:]
}
}
if ipv6.Length == 0 {
return fmt.Errorf("IPv6 length 0, but next header is %v, not HopByHop", ipv6.NextHeader)
}
pEnd := int(ipv6.Length)
if pEnd > len(ipv6.Payload) {
df.SetTruncated()
pEnd = len(ipv6.Payload)
}
ipv6.Payload = ipv6.Payload[:pEnd]
return nil
}
// CanDecode implementation according to gopacket.DecodingLayer
func (ipv6 *IPv6) CanDecode() gopacket.LayerClass {
return LayerTypeIPv6
}
// NextLayerType implementation according to gopacket.DecodingLayer
func (ipv6 *IPv6) NextLayerType() gopacket.LayerType {
if ipv6.HopByHop != nil {
return ipv6.HopByHop.NextHeader.LayerType()
}
return ipv6.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, df gopacket.DecodeFeedback) (h *ipv6HeaderTLVOption, _ error) {
if len(data) < 2 {
df.SetTruncated()
return nil, errors.New("IPv6 header option too small")
}
h = &ipv6HeaderTLVOption{}
if data[0] == 0 {
h.ActualLength = 1
return
}
h.OptionType = data[0]
h.OptionLength = data[1]
h.ActualLength = int(h.OptionLength) + 2
if len(data) < h.ActualLength {
df.SetTruncated()
return nil, errors.New("IPv6 header TLV option too small")
}
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, df gopacket.DecodeFeedback) (i ipv6ExtensionBase, returnedErr error) {
if len(data) < 2 {
df.SetTruncated()
return ipv6ExtensionBase{}, fmt.Errorf("Invalid ip6-extension header. Length %d less than 2", len(data))
}
i.NextHeader = IPProtocol(data[0])
i.HeaderLength = data[1]
i.ActualLength = int(i.HeaderLength)*8 + 8
if len(data) < i.ActualLength {
return ipv6ExtensionBase{}, fmt.Errorf("Invalid ip6-extension header. Length %d less than specified length %d", len(data), i.ActualLength)
}
i.Contents = data[:i.ActualLength]
i.Payload = data[i.ActualLength:]
return
}
// 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
}
// DecodeFromBytes implementation according to gopacket.DecodingLayer
func (i *IPv6ExtensionSkipper) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
extension, err := decodeIPv6ExtensionBase(data, df)
if err != nil {
return err
}
i.BaseLayer = BaseLayer{data[:extension.ActualLength], data[extension.ActualLength:]}
i.NextHeader = extension.NextHeader
return nil
}
// CanDecode implementation according to gopacket.DecodingLayer
func (i *IPv6ExtensionSkipper) CanDecode() gopacket.LayerClass {
return LayerClassIPv6Extension
}
// NextLayerType implementation according to gopacket.DecodingLayer
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 }
// SerializeTo implementation according to gopacket.SerializableLayer
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
}
// DecodeFromBytes implementation according to gopacket.DecodingLayer
func (i *IPv6HopByHop) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
var err error
i.ipv6ExtensionBase, err = decodeIPv6ExtensionBase(data, df)
if err != nil {
return err
}
i.Options = i.Options[:0]
offset := 2
for offset < i.ActualLength {
opt, err := decodeIPv6HeaderTLVOption(data[offset:], df)
if err != nil {
return err
}
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)
}
// SetJumboLength adds the IPv6HopByHopOptionJumbogram with the given length
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 {
base, err := decodeIPv6ExtensionBase(data, p)
if err != nil {
return err
}
i := &IPv6Routing{
ipv6ExtensionBase: base,
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 {
if len(data) < 8 {
p.SetTruncated()
return fmt.Errorf("Invalid ip6-fragment header. Length %d less than 8", len(data))
}
i := &IPv6Fragment{
BaseLayer: BaseLayer{data[:8], data[8:]},
NextHeader: IPProtocol(data[0]),
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 }
// DecodeFromBytes implementation according to gopacket.DecodingLayer
func (i *IPv6Destination) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
var err error
i.ipv6ExtensionBase, err = decodeIPv6ExtensionBase(data, df)
if err != nil {
return err
}
offset := 2
for offset < i.ActualLength {
opt, err := decodeIPv6HeaderTLVOption(data[offset:], df)
if err != nil {
return err
}
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)
}
// AddressTo16 ensures IPv6.SrcIP and IPv6.DstIP are actually IPv6 addresses (i.e. 16 byte addresses)
func (ipv6 *IPv6) AddressTo16() error {
if err := checkIPv6Address(ipv6.SrcIP); err != nil {
return fmt.Errorf("Invalid source IPv6 address (%s)", err)
}
if err := checkIPv6Address(ipv6.DstIP); err != nil {
return fmt.Errorf("Invalid destination IPv6 address (%s)", err)
}
return nil
}
+77
View File
@@ -0,0 +1,77 @@
// 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"
"errors"
"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 {
if len(data) < 12 {
p.SetTruncated()
return errors.New("IPSec AH packet less than 12 bytes")
}
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
if len(data) < i.ActualLength {
p.SetTruncated()
return errors.New("Truncated AH packet < ActualLength")
}
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:],
}
p.AddLayer(i)
return nil
}
+223
View File
@@ -0,0 +1,223 @@
// 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"
)
var (
LayerTypeARP = gopacket.RegisterLayerType(10, gopacket.LayerTypeMetadata{Name: "ARP", Decoder: gopacket.DecodeFunc(decodeARP)})
LayerTypeCiscoDiscovery = gopacket.RegisterLayerType(11, gopacket.LayerTypeMetadata{Name: "CiscoDiscovery", Decoder: gopacket.DecodeFunc(decodeCiscoDiscovery)})
LayerTypeEthernetCTP = gopacket.RegisterLayerType(12, gopacket.LayerTypeMetadata{Name: "EthernetCTP", Decoder: gopacket.DecodeFunc(decodeEthernetCTP)})
LayerTypeEthernetCTPForwardData = gopacket.RegisterLayerType(13, gopacket.LayerTypeMetadata{Name: "EthernetCTPForwardData", Decoder: nil})
LayerTypeEthernetCTPReply = gopacket.RegisterLayerType(14, gopacket.LayerTypeMetadata{Name: "EthernetCTPReply", Decoder: nil})
LayerTypeDot1Q = gopacket.RegisterLayerType(15, gopacket.LayerTypeMetadata{Name: "Dot1Q", Decoder: gopacket.DecodeFunc(decodeDot1Q)})
LayerTypeEtherIP = gopacket.RegisterLayerType(16, gopacket.LayerTypeMetadata{Name: "EtherIP", Decoder: gopacket.DecodeFunc(decodeEtherIP)})
LayerTypeEthernet = gopacket.RegisterLayerType(17, gopacket.LayerTypeMetadata{Name: "Ethernet", Decoder: gopacket.DecodeFunc(decodeEthernet)})
LayerTypeGRE = gopacket.RegisterLayerType(18, gopacket.LayerTypeMetadata{Name: "GRE", Decoder: gopacket.DecodeFunc(decodeGRE)})
LayerTypeICMPv4 = gopacket.RegisterLayerType(19, gopacket.LayerTypeMetadata{Name: "ICMPv4", Decoder: gopacket.DecodeFunc(decodeICMPv4)})
LayerTypeIPv4 = gopacket.RegisterLayerType(20, gopacket.LayerTypeMetadata{Name: "IPv4", Decoder: gopacket.DecodeFunc(decodeIPv4)})
LayerTypeIPv6 = gopacket.RegisterLayerType(21, gopacket.LayerTypeMetadata{Name: "IPv6", Decoder: gopacket.DecodeFunc(decodeIPv6)})
LayerTypeLLC = gopacket.RegisterLayerType(22, gopacket.LayerTypeMetadata{Name: "LLC", Decoder: gopacket.DecodeFunc(decodeLLC)})
LayerTypeSNAP = gopacket.RegisterLayerType(23, gopacket.LayerTypeMetadata{Name: "SNAP", Decoder: gopacket.DecodeFunc(decodeSNAP)})
LayerTypeMPLS = gopacket.RegisterLayerType(24, gopacket.LayerTypeMetadata{Name: "MPLS", Decoder: gopacket.DecodeFunc(decodeMPLS)})
LayerTypePPP = gopacket.RegisterLayerType(25, gopacket.LayerTypeMetadata{Name: "PPP", Decoder: gopacket.DecodeFunc(decodePPP)})
LayerTypePPPoE = gopacket.RegisterLayerType(26, gopacket.LayerTypeMetadata{Name: "PPPoE", Decoder: gopacket.DecodeFunc(decodePPPoE)})
LayerTypeRUDP = gopacket.RegisterLayerType(27, gopacket.LayerTypeMetadata{Name: "RUDP", Decoder: gopacket.DecodeFunc(decodeRUDP)})
LayerTypeSCTP = gopacket.RegisterLayerType(28, gopacket.LayerTypeMetadata{Name: "SCTP", Decoder: gopacket.DecodeFunc(decodeSCTP)})
LayerTypeSCTPUnknownChunkType = gopacket.RegisterLayerType(29, gopacket.LayerTypeMetadata{Name: "SCTPUnknownChunkType", Decoder: nil})
LayerTypeSCTPData = gopacket.RegisterLayerType(30, gopacket.LayerTypeMetadata{Name: "SCTPData", Decoder: nil})
LayerTypeSCTPInit = gopacket.RegisterLayerType(31, gopacket.LayerTypeMetadata{Name: "SCTPInit", Decoder: nil})
LayerTypeSCTPSack = gopacket.RegisterLayerType(32, gopacket.LayerTypeMetadata{Name: "SCTPSack", Decoder: nil})
LayerTypeSCTPHeartbeat = gopacket.RegisterLayerType(33, gopacket.LayerTypeMetadata{Name: "SCTPHeartbeat", Decoder: nil})
LayerTypeSCTPError = gopacket.RegisterLayerType(34, gopacket.LayerTypeMetadata{Name: "SCTPError", Decoder: nil})
LayerTypeSCTPShutdown = gopacket.RegisterLayerType(35, gopacket.LayerTypeMetadata{Name: "SCTPShutdown", Decoder: nil})
LayerTypeSCTPShutdownAck = gopacket.RegisterLayerType(36, gopacket.LayerTypeMetadata{Name: "SCTPShutdownAck", Decoder: nil})
LayerTypeSCTPCookieEcho = gopacket.RegisterLayerType(37, gopacket.LayerTypeMetadata{Name: "SCTPCookieEcho", Decoder: nil})
LayerTypeSCTPEmptyLayer = gopacket.RegisterLayerType(38, gopacket.LayerTypeMetadata{Name: "SCTPEmptyLayer", Decoder: nil})
LayerTypeSCTPInitAck = gopacket.RegisterLayerType(39, gopacket.LayerTypeMetadata{Name: "SCTPInitAck", Decoder: nil})
LayerTypeSCTPHeartbeatAck = gopacket.RegisterLayerType(40, gopacket.LayerTypeMetadata{Name: "SCTPHeartbeatAck", Decoder: nil})
LayerTypeSCTPAbort = gopacket.RegisterLayerType(41, gopacket.LayerTypeMetadata{Name: "SCTPAbort", Decoder: nil})
LayerTypeSCTPShutdownComplete = gopacket.RegisterLayerType(42, gopacket.LayerTypeMetadata{Name: "SCTPShutdownComplete", Decoder: nil})
LayerTypeSCTPCookieAck = gopacket.RegisterLayerType(43, gopacket.LayerTypeMetadata{Name: "SCTPCookieAck", Decoder: nil})
LayerTypeTCP = gopacket.RegisterLayerType(44, gopacket.LayerTypeMetadata{Name: "TCP", Decoder: gopacket.DecodeFunc(decodeTCP)})
LayerTypeUDP = gopacket.RegisterLayerType(45, gopacket.LayerTypeMetadata{Name: "UDP", Decoder: gopacket.DecodeFunc(decodeUDP)})
LayerTypeIPv6HopByHop = gopacket.RegisterLayerType(46, gopacket.LayerTypeMetadata{Name: "IPv6HopByHop", Decoder: gopacket.DecodeFunc(decodeIPv6HopByHop)})
LayerTypeIPv6Routing = gopacket.RegisterLayerType(47, gopacket.LayerTypeMetadata{Name: "IPv6Routing", Decoder: gopacket.DecodeFunc(decodeIPv6Routing)})
LayerTypeIPv6Fragment = gopacket.RegisterLayerType(48, gopacket.LayerTypeMetadata{Name: "IPv6Fragment", Decoder: gopacket.DecodeFunc(decodeIPv6Fragment)})
LayerTypeIPv6Destination = gopacket.RegisterLayerType(49, gopacket.LayerTypeMetadata{Name: "IPv6Destination", Decoder: gopacket.DecodeFunc(decodeIPv6Destination)})
LayerTypeIPSecAH = gopacket.RegisterLayerType(50, gopacket.LayerTypeMetadata{Name: "IPSecAH", Decoder: gopacket.DecodeFunc(decodeIPSecAH)})
LayerTypeIPSecESP = gopacket.RegisterLayerType(51, gopacket.LayerTypeMetadata{Name: "IPSecESP", Decoder: gopacket.DecodeFunc(decodeIPSecESP)})
LayerTypeUDPLite = gopacket.RegisterLayerType(52, gopacket.LayerTypeMetadata{Name: "UDPLite", Decoder: gopacket.DecodeFunc(decodeUDPLite)})
LayerTypeFDDI = gopacket.RegisterLayerType(53, gopacket.LayerTypeMetadata{Name: "FDDI", Decoder: gopacket.DecodeFunc(decodeFDDI)})
LayerTypeLoopback = gopacket.RegisterLayerType(54, gopacket.LayerTypeMetadata{Name: "Loopback", Decoder: gopacket.DecodeFunc(decodeLoopback)})
LayerTypeEAP = gopacket.RegisterLayerType(55, gopacket.LayerTypeMetadata{Name: "EAP", Decoder: gopacket.DecodeFunc(decodeEAP)})
LayerTypeEAPOL = gopacket.RegisterLayerType(56, gopacket.LayerTypeMetadata{Name: "EAPOL", Decoder: gopacket.DecodeFunc(decodeEAPOL)})
LayerTypeICMPv6 = gopacket.RegisterLayerType(57, gopacket.LayerTypeMetadata{Name: "ICMPv6", Decoder: gopacket.DecodeFunc(decodeICMPv6)})
LayerTypeLinkLayerDiscovery = gopacket.RegisterLayerType(58, gopacket.LayerTypeMetadata{Name: "LinkLayerDiscovery", Decoder: gopacket.DecodeFunc(decodeLinkLayerDiscovery)})
LayerTypeCiscoDiscoveryInfo = gopacket.RegisterLayerType(59, gopacket.LayerTypeMetadata{Name: "CiscoDiscoveryInfo", Decoder: gopacket.DecodeFunc(decodeCiscoDiscoveryInfo)})
LayerTypeLinkLayerDiscoveryInfo = gopacket.RegisterLayerType(60, gopacket.LayerTypeMetadata{Name: "LinkLayerDiscoveryInfo", Decoder: nil})
LayerTypeNortelDiscovery = gopacket.RegisterLayerType(61, gopacket.LayerTypeMetadata{Name: "NortelDiscovery", Decoder: gopacket.DecodeFunc(decodeNortelDiscovery)})
LayerTypeIGMP = gopacket.RegisterLayerType(62, gopacket.LayerTypeMetadata{Name: "IGMP", Decoder: gopacket.DecodeFunc(decodeIGMP)})
LayerTypePFLog = gopacket.RegisterLayerType(63, gopacket.LayerTypeMetadata{Name: "PFLog", Decoder: gopacket.DecodeFunc(decodePFLog)})
LayerTypeRadioTap = gopacket.RegisterLayerType(64, gopacket.LayerTypeMetadata{Name: "RadioTap", Decoder: gopacket.DecodeFunc(decodeRadioTap)})
LayerTypeDot11 = gopacket.RegisterLayerType(65, gopacket.LayerTypeMetadata{Name: "Dot11", Decoder: gopacket.DecodeFunc(decodeDot11)})
LayerTypeDot11Ctrl = gopacket.RegisterLayerType(66, gopacket.LayerTypeMetadata{Name: "Dot11Ctrl", Decoder: gopacket.DecodeFunc(decodeDot11Ctrl)})
LayerTypeDot11Data = gopacket.RegisterLayerType(67, gopacket.LayerTypeMetadata{Name: "Dot11Data", Decoder: gopacket.DecodeFunc(decodeDot11Data)})
LayerTypeDot11DataCFAck = gopacket.RegisterLayerType(68, gopacket.LayerTypeMetadata{Name: "Dot11DataCFAck", Decoder: gopacket.DecodeFunc(decodeDot11DataCFAck)})
LayerTypeDot11DataCFPoll = gopacket.RegisterLayerType(69, gopacket.LayerTypeMetadata{Name: "Dot11DataCFPoll", Decoder: gopacket.DecodeFunc(decodeDot11DataCFPoll)})
LayerTypeDot11DataCFAckPoll = gopacket.RegisterLayerType(70, gopacket.LayerTypeMetadata{Name: "Dot11DataCFAckPoll", Decoder: gopacket.DecodeFunc(decodeDot11DataCFAckPoll)})
LayerTypeDot11DataNull = gopacket.RegisterLayerType(71, gopacket.LayerTypeMetadata{Name: "Dot11DataNull", Decoder: gopacket.DecodeFunc(decodeDot11DataNull)})
LayerTypeDot11DataCFAckNoData = gopacket.RegisterLayerType(72, gopacket.LayerTypeMetadata{Name: "Dot11DataCFAck", Decoder: gopacket.DecodeFunc(decodeDot11DataCFAck)})
LayerTypeDot11DataCFPollNoData = gopacket.RegisterLayerType(73, gopacket.LayerTypeMetadata{Name: "Dot11DataCFPoll", Decoder: gopacket.DecodeFunc(decodeDot11DataCFPoll)})
LayerTypeDot11DataCFAckPollNoData = gopacket.RegisterLayerType(74, gopacket.LayerTypeMetadata{Name: "Dot11DataCFAckPoll", Decoder: gopacket.DecodeFunc(decodeDot11DataCFAckPoll)})
LayerTypeDot11DataQOSData = gopacket.RegisterLayerType(75, gopacket.LayerTypeMetadata{Name: "Dot11DataQOSData", Decoder: gopacket.DecodeFunc(decodeDot11DataQOSData)})
LayerTypeDot11DataQOSDataCFAck = gopacket.RegisterLayerType(76, gopacket.LayerTypeMetadata{Name: "Dot11DataQOSDataCFAck", Decoder: gopacket.DecodeFunc(decodeDot11DataQOSDataCFAck)})
LayerTypeDot11DataQOSDataCFPoll = gopacket.RegisterLayerType(77, gopacket.LayerTypeMetadata{Name: "Dot11DataQOSDataCFPoll", Decoder: gopacket.DecodeFunc(decodeDot11DataQOSDataCFPoll)})
LayerTypeDot11DataQOSDataCFAckPoll = gopacket.RegisterLayerType(78, gopacket.LayerTypeMetadata{Name: "Dot11DataQOSDataCFAckPoll", Decoder: gopacket.DecodeFunc(decodeDot11DataQOSDataCFAckPoll)})
LayerTypeDot11DataQOSNull = gopacket.RegisterLayerType(79, gopacket.LayerTypeMetadata{Name: "Dot11DataQOSNull", Decoder: gopacket.DecodeFunc(decodeDot11DataQOSNull)})
LayerTypeDot11DataQOSCFPollNoData = gopacket.RegisterLayerType(80, gopacket.LayerTypeMetadata{Name: "Dot11DataQOSCFPoll", Decoder: gopacket.DecodeFunc(decodeDot11DataQOSCFPollNoData)})
LayerTypeDot11DataQOSCFAckPollNoData = gopacket.RegisterLayerType(81, gopacket.LayerTypeMetadata{Name: "Dot11DataQOSCFAckPoll", Decoder: gopacket.DecodeFunc(decodeDot11DataQOSCFAckPollNoData)})
LayerTypeDot11InformationElement = gopacket.RegisterLayerType(82, gopacket.LayerTypeMetadata{Name: "Dot11InformationElement", Decoder: gopacket.DecodeFunc(decodeDot11InformationElement)})
LayerTypeDot11CtrlCTS = gopacket.RegisterLayerType(83, gopacket.LayerTypeMetadata{Name: "Dot11CtrlCTS", Decoder: gopacket.DecodeFunc(decodeDot11CtrlCTS)})
LayerTypeDot11CtrlRTS = gopacket.RegisterLayerType(84, gopacket.LayerTypeMetadata{Name: "Dot11CtrlRTS", Decoder: gopacket.DecodeFunc(decodeDot11CtrlRTS)})
LayerTypeDot11CtrlBlockAckReq = gopacket.RegisterLayerType(85, gopacket.LayerTypeMetadata{Name: "Dot11CtrlBlockAckReq", Decoder: gopacket.DecodeFunc(decodeDot11CtrlBlockAckReq)})
LayerTypeDot11CtrlBlockAck = gopacket.RegisterLayerType(86, gopacket.LayerTypeMetadata{Name: "Dot11CtrlBlockAck", Decoder: gopacket.DecodeFunc(decodeDot11CtrlBlockAck)})
LayerTypeDot11CtrlPowersavePoll = gopacket.RegisterLayerType(87, gopacket.LayerTypeMetadata{Name: "Dot11CtrlPowersavePoll", Decoder: gopacket.DecodeFunc(decodeDot11CtrlPowersavePoll)})
LayerTypeDot11CtrlAck = gopacket.RegisterLayerType(88, gopacket.LayerTypeMetadata{Name: "Dot11CtrlAck", Decoder: gopacket.DecodeFunc(decodeDot11CtrlAck)})
LayerTypeDot11CtrlCFEnd = gopacket.RegisterLayerType(89, gopacket.LayerTypeMetadata{Name: "Dot11CtrlCFEnd", Decoder: gopacket.DecodeFunc(decodeDot11CtrlCFEnd)})
LayerTypeDot11CtrlCFEndAck = gopacket.RegisterLayerType(90, gopacket.LayerTypeMetadata{Name: "Dot11CtrlCFEndAck", Decoder: gopacket.DecodeFunc(decodeDot11CtrlCFEndAck)})
LayerTypeDot11MgmtAssociationReq = gopacket.RegisterLayerType(91, gopacket.LayerTypeMetadata{Name: "Dot11MgmtAssociationReq", Decoder: gopacket.DecodeFunc(decodeDot11MgmtAssociationReq)})
LayerTypeDot11MgmtAssociationResp = gopacket.RegisterLayerType(92, gopacket.LayerTypeMetadata{Name: "Dot11MgmtAssociationResp", Decoder: gopacket.DecodeFunc(decodeDot11MgmtAssociationResp)})
LayerTypeDot11MgmtReassociationReq = gopacket.RegisterLayerType(93, gopacket.LayerTypeMetadata{Name: "Dot11MgmtReassociationReq", Decoder: gopacket.DecodeFunc(decodeDot11MgmtReassociationReq)})
LayerTypeDot11MgmtReassociationResp = gopacket.RegisterLayerType(94, gopacket.LayerTypeMetadata{Name: "Dot11MgmtReassociationResp", Decoder: gopacket.DecodeFunc(decodeDot11MgmtReassociationResp)})
LayerTypeDot11MgmtProbeReq = gopacket.RegisterLayerType(95, gopacket.LayerTypeMetadata{Name: "Dot11MgmtProbeReq", Decoder: gopacket.DecodeFunc(decodeDot11MgmtProbeReq)})
LayerTypeDot11MgmtProbeResp = gopacket.RegisterLayerType(96, gopacket.LayerTypeMetadata{Name: "Dot11MgmtProbeResp", Decoder: gopacket.DecodeFunc(decodeDot11MgmtProbeResp)})
LayerTypeDot11MgmtMeasurementPilot = gopacket.RegisterLayerType(97, gopacket.LayerTypeMetadata{Name: "Dot11MgmtMeasurementPilot", Decoder: gopacket.DecodeFunc(decodeDot11MgmtMeasurementPilot)})
LayerTypeDot11MgmtBeacon = gopacket.RegisterLayerType(98, gopacket.LayerTypeMetadata{Name: "Dot11MgmtBeacon", Decoder: gopacket.DecodeFunc(decodeDot11MgmtBeacon)})
LayerTypeDot11MgmtATIM = gopacket.RegisterLayerType(99, gopacket.LayerTypeMetadata{Name: "Dot11MgmtATIM", Decoder: gopacket.DecodeFunc(decodeDot11MgmtATIM)})
LayerTypeDot11MgmtDisassociation = gopacket.RegisterLayerType(100, gopacket.LayerTypeMetadata{Name: "Dot11MgmtDisassociation", Decoder: gopacket.DecodeFunc(decodeDot11MgmtDisassociation)})
LayerTypeDot11MgmtAuthentication = gopacket.RegisterLayerType(101, gopacket.LayerTypeMetadata{Name: "Dot11MgmtAuthentication", Decoder: gopacket.DecodeFunc(decodeDot11MgmtAuthentication)})
LayerTypeDot11MgmtDeauthentication = gopacket.RegisterLayerType(102, gopacket.LayerTypeMetadata{Name: "Dot11MgmtDeauthentication", Decoder: gopacket.DecodeFunc(decodeDot11MgmtDeauthentication)})
LayerTypeDot11MgmtAction = gopacket.RegisterLayerType(103, gopacket.LayerTypeMetadata{Name: "Dot11MgmtAction", Decoder: gopacket.DecodeFunc(decodeDot11MgmtAction)})
LayerTypeDot11MgmtActionNoAck = gopacket.RegisterLayerType(104, gopacket.LayerTypeMetadata{Name: "Dot11MgmtActionNoAck", Decoder: gopacket.DecodeFunc(decodeDot11MgmtActionNoAck)})
LayerTypeDot11MgmtArubaWLAN = gopacket.RegisterLayerType(105, gopacket.LayerTypeMetadata{Name: "Dot11MgmtArubaWLAN", Decoder: gopacket.DecodeFunc(decodeDot11MgmtArubaWLAN)})
LayerTypeDot11WEP = gopacket.RegisterLayerType(106, gopacket.LayerTypeMetadata{Name: "Dot11WEP", Decoder: gopacket.DecodeFunc(decodeDot11WEP)})
LayerTypeDNS = gopacket.RegisterLayerType(107, gopacket.LayerTypeMetadata{Name: "DNS", Decoder: gopacket.DecodeFunc(decodeDNS)})
LayerTypeUSB = gopacket.RegisterLayerType(108, gopacket.LayerTypeMetadata{Name: "USB", Decoder: gopacket.DecodeFunc(decodeUSB)})
LayerTypeUSBRequestBlockSetup = gopacket.RegisterLayerType(109, gopacket.LayerTypeMetadata{Name: "USBRequestBlockSetup", Decoder: gopacket.DecodeFunc(decodeUSBRequestBlockSetup)})
LayerTypeUSBControl = gopacket.RegisterLayerType(110, gopacket.LayerTypeMetadata{Name: "USBControl", Decoder: gopacket.DecodeFunc(decodeUSBControl)})
LayerTypeUSBInterrupt = gopacket.RegisterLayerType(111, gopacket.LayerTypeMetadata{Name: "USBInterrupt", Decoder: gopacket.DecodeFunc(decodeUSBInterrupt)})
LayerTypeUSBBulk = gopacket.RegisterLayerType(112, gopacket.LayerTypeMetadata{Name: "USBBulk", Decoder: gopacket.DecodeFunc(decodeUSBBulk)})
LayerTypeLinuxSLL = gopacket.RegisterLayerType(113, gopacket.LayerTypeMetadata{Name: "Linux SLL", Decoder: gopacket.DecodeFunc(decodeLinuxSLL)})
LayerTypeSFlow = gopacket.RegisterLayerType(114, gopacket.LayerTypeMetadata{Name: "SFlow", Decoder: gopacket.DecodeFunc(decodeSFlow)})
LayerTypePrismHeader = gopacket.RegisterLayerType(115, gopacket.LayerTypeMetadata{Name: "Prism monitor mode header", Decoder: gopacket.DecodeFunc(decodePrismHeader)})
LayerTypeVXLAN = gopacket.RegisterLayerType(116, gopacket.LayerTypeMetadata{Name: "VXLAN", Decoder: gopacket.DecodeFunc(decodeVXLAN)})
LayerTypeNTP = gopacket.RegisterLayerType(117, gopacket.LayerTypeMetadata{Name: "NTP", Decoder: gopacket.DecodeFunc(decodeNTP)})
LayerTypeDHCPv4 = gopacket.RegisterLayerType(118, gopacket.LayerTypeMetadata{Name: "DHCPv4", Decoder: gopacket.DecodeFunc(decodeDHCPv4)})
LayerTypeVRRP = gopacket.RegisterLayerType(119, gopacket.LayerTypeMetadata{Name: "VRRP", Decoder: gopacket.DecodeFunc(decodeVRRP)})
LayerTypeGeneve = gopacket.RegisterLayerType(120, gopacket.LayerTypeMetadata{Name: "Geneve", Decoder: gopacket.DecodeFunc(decodeGeneve)})
LayerTypeSTP = gopacket.RegisterLayerType(121, gopacket.LayerTypeMetadata{Name: "STP", Decoder: gopacket.DecodeFunc(decodeSTP)})
LayerTypeBFD = gopacket.RegisterLayerType(122, gopacket.LayerTypeMetadata{Name: "BFD", Decoder: gopacket.DecodeFunc(decodeBFD)})
LayerTypeOSPF = gopacket.RegisterLayerType(123, gopacket.LayerTypeMetadata{Name: "OSPF", Decoder: gopacket.DecodeFunc(decodeOSPF)})
LayerTypeICMPv6RouterSolicitation = gopacket.RegisterLayerType(124, gopacket.LayerTypeMetadata{Name: "ICMPv6RouterSolicitation", Decoder: gopacket.DecodeFunc(decodeICMPv6RouterSolicitation)})
LayerTypeICMPv6RouterAdvertisement = gopacket.RegisterLayerType(125, gopacket.LayerTypeMetadata{Name: "ICMPv6RouterAdvertisement", Decoder: gopacket.DecodeFunc(decodeICMPv6RouterAdvertisement)})
LayerTypeICMPv6NeighborSolicitation = gopacket.RegisterLayerType(126, gopacket.LayerTypeMetadata{Name: "ICMPv6NeighborSolicitation", Decoder: gopacket.DecodeFunc(decodeICMPv6NeighborSolicitation)})
LayerTypeICMPv6NeighborAdvertisement = gopacket.RegisterLayerType(127, gopacket.LayerTypeMetadata{Name: "ICMPv6NeighborAdvertisement", Decoder: gopacket.DecodeFunc(decodeICMPv6NeighborAdvertisement)})
LayerTypeICMPv6Redirect = gopacket.RegisterLayerType(128, gopacket.LayerTypeMetadata{Name: "ICMPv6Redirect", Decoder: gopacket.DecodeFunc(decodeICMPv6Redirect)})
LayerTypeGTPv1U = gopacket.RegisterLayerType(129, gopacket.LayerTypeMetadata{Name: "GTPv1U", Decoder: gopacket.DecodeFunc(decodeGTPv1u)})
LayerTypeEAPOLKey = gopacket.RegisterLayerType(130, gopacket.LayerTypeMetadata{Name: "EAPOLKey", Decoder: gopacket.DecodeFunc(decodeEAPOLKey)})
LayerTypeLCM = gopacket.RegisterLayerType(131, gopacket.LayerTypeMetadata{Name: "LCM", Decoder: gopacket.DecodeFunc(decodeLCM)})
LayerTypeICMPv6Echo = gopacket.RegisterLayerType(132, gopacket.LayerTypeMetadata{Name: "ICMPv6Echo", Decoder: gopacket.DecodeFunc(decodeICMPv6Echo)})
LayerTypeSIP = gopacket.RegisterLayerType(133, gopacket.LayerTypeMetadata{Name: "SIP", Decoder: gopacket.DecodeFunc(decodeSIP)})
LayerTypeDHCPv6 = gopacket.RegisterLayerType(134, gopacket.LayerTypeMetadata{Name: "DHCPv6", Decoder: gopacket.DecodeFunc(decodeDHCPv6)})
LayerTypeMLDv1MulticastListenerReport = gopacket.RegisterLayerType(135, gopacket.LayerTypeMetadata{Name: "MLDv1MulticastListenerReport", Decoder: gopacket.DecodeFunc(decodeMLDv1MulticastListenerReport)})
LayerTypeMLDv1MulticastListenerDone = gopacket.RegisterLayerType(136, gopacket.LayerTypeMetadata{Name: "MLDv1MulticastListenerDone", Decoder: gopacket.DecodeFunc(decodeMLDv1MulticastListenerDone)})
LayerTypeMLDv1MulticastListenerQuery = gopacket.RegisterLayerType(137, gopacket.LayerTypeMetadata{Name: "MLDv1MulticastListenerQuery", Decoder: gopacket.DecodeFunc(decodeMLDv1MulticastListenerQuery)})
LayerTypeMLDv2MulticastListenerReport = gopacket.RegisterLayerType(138, gopacket.LayerTypeMetadata{Name: "MLDv2MulticastListenerReport", Decoder: gopacket.DecodeFunc(decodeMLDv2MulticastListenerReport)})
LayerTypeMLDv2MulticastListenerQuery = gopacket.RegisterLayerType(139, gopacket.LayerTypeMetadata{Name: "MLDv2MulticastListenerQuery", Decoder: gopacket.DecodeFunc(decodeMLDv2MulticastListenerQuery)})
LayerTypeTLS = gopacket.RegisterLayerType(140, gopacket.LayerTypeMetadata{Name: "TLS", Decoder: gopacket.DecodeFunc(decodeTLS)})
LayerTypeModbusTCP = gopacket.RegisterLayerType(141, gopacket.LayerTypeMetadata{Name: "ModbusTCP", Decoder: gopacket.DecodeFunc(decodeModbusTCP)})
LayerTypeRMCP = gopacket.RegisterLayerType(142, gopacket.LayerTypeMetadata{Name: "RMCP", Decoder: gopacket.DecodeFunc(decodeRMCP)})
LayerTypeASF = gopacket.RegisterLayerType(143, gopacket.LayerTypeMetadata{Name: "ASF", Decoder: gopacket.DecodeFunc(decodeASF)})
LayerTypeASFPresencePong = gopacket.RegisterLayerType(144, gopacket.LayerTypeMetadata{Name: "ASFPresencePong", Decoder: gopacket.DecodeFunc(decodeASFPresencePong)})
LayerTypeERSPANII = gopacket.RegisterLayerType(145, gopacket.LayerTypeMetadata{Name: "ERSPAN Type II", Decoder: gopacket.DecodeFunc(decodeERSPANII)})
LayerTypeRADIUS = gopacket.RegisterLayerType(146, gopacket.LayerTypeMetadata{Name: "RADIUS", Decoder: gopacket.DecodeFunc(decodeRADIUS)})
)
var (
// LayerClassIPNetwork contains TCP/IP network layer types.
LayerClassIPNetwork = gopacket.NewLayerClass([]gopacket.LayerType{
LayerTypeIPv4,
LayerTypeIPv6,
})
// LayerClassIPTransport contains TCP/IP transport layer types.
LayerClassIPTransport = gopacket.NewLayerClass([]gopacket.LayerType{
LayerTypeTCP,
LayerTypeUDP,
LayerTypeSCTP,
})
// LayerClassIPControl contains TCP/IP control protocols.
LayerClassIPControl = gopacket.NewLayerClass([]gopacket.LayerType{
LayerTypeICMPv4,
LayerTypeICMPv6,
})
// LayerClassSCTPChunk contains SCTP chunk types (not the top-level SCTP
// layer).
LayerClassSCTPChunk = gopacket.NewLayerClass([]gopacket.LayerType{
LayerTypeSCTPUnknownChunkType,
LayerTypeSCTPData,
LayerTypeSCTPInit,
LayerTypeSCTPSack,
LayerTypeSCTPHeartbeat,
LayerTypeSCTPError,
LayerTypeSCTPShutdown,
LayerTypeSCTPShutdownAck,
LayerTypeSCTPCookieEcho,
LayerTypeSCTPEmptyLayer,
LayerTypeSCTPInitAck,
LayerTypeSCTPHeartbeatAck,
LayerTypeSCTPAbort,
LayerTypeSCTPShutdownComplete,
LayerTypeSCTPCookieAck,
})
// LayerClassIPv6Extension contains IPv6 extension headers.
LayerClassIPv6Extension = gopacket.NewLayerClass([]gopacket.LayerType{
LayerTypeIPv6HopByHop,
LayerTypeIPv6Routing,
LayerTypeIPv6Fragment,
LayerTypeIPv6Destination,
})
LayerClassIPSec = gopacket.NewLayerClass([]gopacket.LayerType{
LayerTypeIPSecAH,
LayerTypeIPSecESP,
})
// LayerClassICMPv6NDP contains ICMPv6 neighbor discovery protocol
// messages.
LayerClassICMPv6NDP = gopacket.NewLayerClass([]gopacket.LayerType{
LayerTypeICMPv6RouterSolicitation,
LayerTypeICMPv6RouterAdvertisement,
LayerTypeICMPv6NeighborSolicitation,
LayerTypeICMPv6NeighborAdvertisement,
LayerTypeICMPv6Redirect,
})
// LayerClassMLDv1 contains multicast listener discovery protocol
LayerClassMLDv1 = gopacket.NewLayerClass([]gopacket.LayerType{
LayerTypeMLDv1MulticastListenerQuery,
LayerTypeMLDv1MulticastListenerReport,
LayerTypeMLDv1MulticastListenerDone,
})
// LayerClassMLDv2 contains multicast listener discovery protocol v2
LayerClassMLDv2 = gopacket.NewLayerClass([]gopacket.LayerType{
LayerTypeMLDv1MulticastListenerReport,
LayerTypeMLDv1MulticastListenerDone,
LayerTypeMLDv2MulticastListenerReport,
LayerTypeMLDv1MulticastListenerQuery,
LayerTypeMLDv2MulticastListenerQuery,
})
)

Some files were not shown because too many files have changed in this diff Show More