TUN-6666: Define packet package
This package defines IP and ICMP packet, decoders, encoder and flow
This commit is contained in:
parent
20ed7557f9
commit
bad2e8e812
|
@ -20,6 +20,7 @@ import (
|
||||||
|
|
||||||
"github.com/cloudflare/cloudflared/datagramsession"
|
"github.com/cloudflare/cloudflared/datagramsession"
|
||||||
"github.com/cloudflare/cloudflared/ingress"
|
"github.com/cloudflare/cloudflared/ingress"
|
||||||
|
"github.com/cloudflare/cloudflared/packet"
|
||||||
quicpogs "github.com/cloudflare/cloudflared/quic"
|
quicpogs "github.com/cloudflare/cloudflared/quic"
|
||||||
"github.com/cloudflare/cloudflared/tracing"
|
"github.com/cloudflare/cloudflared/tracing"
|
||||||
tunnelpogs "github.com/cloudflare/cloudflared/tunnelrpc/pogs"
|
tunnelpogs "github.com/cloudflare/cloudflared/tunnelrpc/pogs"
|
||||||
|
@ -66,9 +67,9 @@ func NewQUICConnection(
|
||||||
return nil, &EdgeQuicDialError{Cause: err}
|
return nil, &EdgeQuicDialError{Cause: err}
|
||||||
}
|
}
|
||||||
|
|
||||||
demuxChan := make(chan *quicpogs.SessionDatagram, demuxChanCapacity)
|
demuxChan := make(chan *packet.Session, demuxChanCapacity)
|
||||||
datagramMuxer := quicpogs.NewDatagramMuxer(session, logger, demuxChan)
|
datagramMuxer := quicpogs.NewDatagramMuxer(session, logger, demuxChan)
|
||||||
sessionManager := datagramsession.NewManager(logger, datagramMuxer.MuxSession, demuxChan)
|
sessionManager := datagramsession.NewManager(logger, datagramMuxer.SendToSession, demuxChan)
|
||||||
|
|
||||||
return &QUICConnection{
|
return &QUICConnection{
|
||||||
session: session,
|
session: session,
|
||||||
|
|
|
@ -9,7 +9,7 @@ import (
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
"github.com/rs/zerolog"
|
"github.com/rs/zerolog"
|
||||||
|
|
||||||
quicpogs "github.com/cloudflare/cloudflared/quic"
|
"github.com/cloudflare/cloudflared/packet"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -37,7 +37,7 @@ type manager struct {
|
||||||
registrationChan chan *registerSessionEvent
|
registrationChan chan *registerSessionEvent
|
||||||
unregistrationChan chan *unregisterSessionEvent
|
unregistrationChan chan *unregisterSessionEvent
|
||||||
sendFunc transportSender
|
sendFunc transportSender
|
||||||
receiveChan <-chan *quicpogs.SessionDatagram
|
receiveChan <-chan *packet.Session
|
||||||
closedChan <-chan struct{}
|
closedChan <-chan struct{}
|
||||||
sessions map[uuid.UUID]*Session
|
sessions map[uuid.UUID]*Session
|
||||||
log *zerolog.Logger
|
log *zerolog.Logger
|
||||||
|
@ -45,7 +45,7 @@ type manager struct {
|
||||||
timeout time.Duration
|
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{
|
return &manager{
|
||||||
registrationChan: make(chan *registerSessionEvent),
|
registrationChan: make(chan *registerSessionEvent),
|
||||||
unregistrationChan: make(chan *unregisterSessionEvent),
|
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]
|
session, ok := m.sessions[datagram.ID]
|
||||||
if !ok {
|
if !ok {
|
||||||
m.log.Error().Str("sessionID", datagram.ID.String()).Msg("session not found")
|
m.log.Error().Str("sessionID", datagram.ID.String()).Msg("session not found")
|
||||||
|
|
|
@ -15,7 +15,7 @@ import (
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
"golang.org/x/sync/errgroup"
|
"golang.org/x/sync/errgroup"
|
||||||
|
|
||||||
quicpogs "github.com/cloudflare/cloudflared/quic"
|
"github.com/cloudflare/cloudflared/packet"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -29,7 +29,7 @@ func TestManagerServe(t *testing.T) {
|
||||||
remoteUnregisterMsg = "eyeball closed connection"
|
remoteUnregisterMsg = "eyeball closed connection"
|
||||||
)
|
)
|
||||||
|
|
||||||
requestChan := make(chan *quicpogs.SessionDatagram)
|
requestChan := make(chan *packet.Session)
|
||||||
transport := mockQUICTransport{
|
transport := mockQUICTransport{
|
||||||
sessions: make(map[uuid.UUID]chan []byte),
|
sessions: make(map[uuid.UUID]chan []byte),
|
||||||
}
|
}
|
||||||
|
@ -241,9 +241,9 @@ type mockQUICTransport struct {
|
||||||
sessions map[uuid.UUID]chan []byte
|
sessions map[uuid.UUID]chan []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
func (me *mockQUICTransport) MuxSession(id uuid.UUID, payload []byte) error {
|
func (me *mockQUICTransport) MuxSession(session *packet.Session) error {
|
||||||
session := me.sessions[id]
|
s := me.sessions[session.ID]
|
||||||
session <- payload
|
s <- session.Payload
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -255,9 +255,9 @@ type mockEyeballSession struct {
|
||||||
respReceiver <-chan []byte
|
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++ {
|
for i := 0; i < me.expectedMsgCount; i++ {
|
||||||
requestChan <- &quicpogs.SessionDatagram{
|
requestChan <- &packet.Session{
|
||||||
ID: me.id,
|
ID: me.id,
|
||||||
Payload: me.expectedMsg,
|
Payload: me.expectedMsg,
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,8 @@ import (
|
||||||
|
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
"github.com/rs/zerolog"
|
"github.com/rs/zerolog"
|
||||||
|
|
||||||
|
"github.com/cloudflare/cloudflared/packet"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -19,7 +21,7 @@ func SessionIdleErr(timeout time.Duration) error {
|
||||||
return fmt.Errorf("session idle for %v", timeout)
|
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
|
// Session is a bidirectional pipe of datagrams between transport and dstConn
|
||||||
// Destination can be a connection with origin or with eyeball
|
// Destination can be a connection with origin or with eyeball
|
||||||
|
@ -101,7 +103,11 @@ func (s *Session) dstToTransport(buffer []byte) (closeSession bool, err error) {
|
||||||
s.markActive()
|
s.markActive()
|
||||||
// https://pkg.go.dev/io#Reader suggests caller should always process n > 0 bytes
|
// https://pkg.go.dev/io#Reader suggests caller should always process n > 0 bytes
|
||||||
if n > 0 || err == nil {
|
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
|
return false, sendErr
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,7 +15,7 @@ import (
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
"golang.org/x/sync/errgroup"
|
"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
|
// 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()
|
cfdConn, originConn := net.Pipe()
|
||||||
payload := testPayload(sessionID)
|
payload := testPayload(sessionID)
|
||||||
|
|
||||||
respChan := make(chan *quicpogs.SessionDatagram)
|
respChan := make(chan *packet.Session)
|
||||||
sender := newMockTransportSender(sessionID, payload)
|
sender := newMockTransportSender(sessionID, payload)
|
||||||
mg := NewManager(&nopLogger, sender.muxSession, respChan)
|
mg := NewManager(&nopLogger, sender.muxSession, respChan)
|
||||||
session := mg.newSession(sessionID, cfdConn)
|
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 {
|
func (mts *mockTransportSender) muxSession(session *packet.Session) error {
|
||||||
if sessionID != mts.expectedSessionID {
|
if session.ID != mts.expectedSessionID {
|
||||||
return fmt.Errorf("Expect session %s, got %s", mts.expectedSessionID, sessionID)
|
return fmt.Errorf("Expect session %s, got %s", mts.expectedSessionID, session.ID)
|
||||||
}
|
}
|
||||||
if !bytes.Equal(payload, mts.expectedPayload) {
|
if !bytes.Equal(session.Payload, mts.expectedPayload) {
|
||||||
return fmt.Errorf("Expect %v, read %v", mts.expectedPayload, payload)
|
return fmt.Errorf("Expect %v, read %v", mts.expectedPayload, session.Payload)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -258,7 +258,7 @@ type sendOnceTransportSender struct {
|
||||||
sentChan chan 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)
|
defer close(sots.sentChan)
|
||||||
return sots.baseSender.muxSession(sessionID, payload)
|
return sots.baseSender.muxSession(session)
|
||||||
}
|
}
|
||||||
|
|
5
go.mod
5
go.mod
|
@ -34,9 +34,9 @@ require (
|
||||||
go.opentelemetry.io/proto/otlp v0.15.0
|
go.opentelemetry.io/proto/otlp v0.15.0
|
||||||
go.uber.org/automaxprocs v1.4.0
|
go.uber.org/automaxprocs v1.4.0
|
||||||
golang.org/x/crypto v0.0.0-20220427172511-eb4f295cb31f
|
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/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
|
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211
|
||||||
google.golang.org/protobuf v1.28.0
|
google.golang.org/protobuf v1.28.0
|
||||||
gopkg.in/coreos/go-oidc.v2 v2.2.1
|
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/httphead v0.0.0-20200921212729-da3d93bc3c58 // indirect
|
||||||
github.com/gobwas/pool v0.2.1 // indirect
|
github.com/gobwas/pool v0.2.1 // indirect
|
||||||
github.com/golang/protobuf v1.5.2 // 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-gateway/v2 v2.7.0 // indirect
|
||||||
github.com/grpc-ecosystem/grpc-opentracing v0.0.0-20180507213350-8e809c8a8645 // indirect
|
github.com/grpc-ecosystem/grpc-opentracing v0.0.0-20180507213350-8e809c8a8645 // indirect
|
||||||
github.com/kylelemons/godebug v1.1.0 // indirect
|
github.com/kylelemons/godebug v1.1.0 // indirect
|
||||||
|
|
6
go.sum
6
go.sum
|
@ -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.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.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||||
github.com/google/gofuzz v1.2.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 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.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=
|
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-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 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-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-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-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||||
golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/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-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 h1:dGzPydgVsqGcTRVwiLJ1jVbufYwmzD3LfVPLKsKg+0k=
|
||||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
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-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-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY=
|
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY=
|
||||||
|
|
|
@ -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 []byte) (*IP, error) {
|
||||||
|
// Should decode to IP layer
|
||||||
|
decoded, err := pd.decodeByVersion(packet)
|
||||||
|
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 []byte) (*ICMP, error) {
|
||||||
|
// Should decode to IP and optionally ICMP layer
|
||||||
|
decoded, err := pd.decodeByVersion(packet)
|
||||||
|
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)
|
||||||
|
}
|
|
@ -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.Data)
|
||||||
|
require.NoError(t, err)
|
||||||
|
assertIPLayer(t, &udp.IP, ipPacket)
|
||||||
|
|
||||||
|
icmpPacket, err := icmpDecoder.Decode(p.Data)
|
||||||
|
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.Data)
|
||||||
|
require.NoError(t, err)
|
||||||
|
if ipPacket.Src.Is4() {
|
||||||
|
assertIPLayer(t, &ipv4Packet, ipPacket)
|
||||||
|
} else {
|
||||||
|
assertIPLayer(t, &ipv6Packet, ipPacket)
|
||||||
|
}
|
||||||
|
icmpPacket, err := icmpDecoder.Decode(p.Data)
|
||||||
|
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(test.packet)
|
||||||
|
require.Error(t, err)
|
||||||
|
require.Nil(t, ipPacket)
|
||||||
|
|
||||||
|
icmpPacket, err := icmpDecoder.Decode(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
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
|
@ -0,0 +1,97 @@
|
||||||
|
package packet
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"net"
|
||||||
|
"net/netip"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
type flowID string
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrFlowNotFound = errors.New("flow not found")
|
||||||
|
)
|
||||||
|
|
||||||
|
func newFlowID(ip net.IP) flowID {
|
||||||
|
return flowID(ip.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
// SrcFlowTracker tracks flow from the perspective of eyeball to origin
|
||||||
|
// flowID is the source IP
|
||||||
|
type SrcFlowTracker struct {
|
||||||
|
lock sync.RWMutex
|
||||||
|
flows map[flowID]*Flow
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewSrcFlowTracker() *SrcFlowTracker {
|
||||||
|
return &SrcFlowTracker{
|
||||||
|
flows: make(map[flowID]*Flow),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sft *SrcFlowTracker) Get(srcIP net.IP) (*Flow, bool) {
|
||||||
|
sft.lock.RLock()
|
||||||
|
defer sft.lock.RUnlock()
|
||||||
|
id := newFlowID(srcIP)
|
||||||
|
flow, ok := sft.flows[id]
|
||||||
|
return flow, ok
|
||||||
|
}
|
||||||
|
|
||||||
|
// Registers a flow. If shouldReplace = true, replace the current flow
|
||||||
|
func (sft *SrcFlowTracker) Register(flow *Flow, shouldReplace bool) (replaced bool) {
|
||||||
|
sft.lock.Lock()
|
||||||
|
defer sft.lock.Unlock()
|
||||||
|
id := flowID(flow.Src.String())
|
||||||
|
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 *SrcFlowTracker) Unregister(flow *Flow, force bool) (forceDeleted bool) {
|
||||||
|
sft.lock.Lock()
|
||||||
|
defer sft.lock.Unlock()
|
||||||
|
id := flowID(flow.Src.String())
|
||||||
|
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
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
package packet
|
||||||
|
|
||||||
|
import "github.com/google/uuid"
|
||||||
|
|
||||||
|
type Session struct {
|
||||||
|
ID uuid.UUID
|
||||||
|
Payload []byte
|
||||||
|
}
|
|
@ -8,21 +8,18 @@ import (
|
||||||
"github.com/lucas-clemente/quic-go"
|
"github.com/lucas-clemente/quic-go"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/rs/zerolog"
|
"github.com/rs/zerolog"
|
||||||
|
|
||||||
|
"github.com/cloudflare/cloudflared/packet"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
sessionIDLen = len(uuid.UUID{})
|
sessionIDLen = len(uuid.UUID{})
|
||||||
)
|
)
|
||||||
|
|
||||||
type SessionDatagram struct {
|
|
||||||
ID uuid.UUID
|
|
||||||
Payload []byte
|
|
||||||
}
|
|
||||||
|
|
||||||
type BaseDatagramMuxer interface {
|
type BaseDatagramMuxer interface {
|
||||||
// MuxSession suffix the session ID to the payload so the other end of the QUIC connection can demultiplex the
|
// SendToSession suffix the session ID to the payload so the other end of the QUIC connection can demultiplex the
|
||||||
// payload from multiple datagram sessions
|
// payload from multiple datagram sessions.
|
||||||
MuxSession(sessionID uuid.UUID, payload []byte) error
|
SendToSession(session *packet.Session) error
|
||||||
// ServeReceive starts a loop to receive datagrams from the QUIC connection
|
// ServeReceive starts a loop to receive datagrams from the QUIC connection
|
||||||
ServeReceive(ctx context.Context) error
|
ServeReceive(ctx context.Context) error
|
||||||
}
|
}
|
||||||
|
@ -30,10 +27,10 @@ type BaseDatagramMuxer interface {
|
||||||
type DatagramMuxer struct {
|
type DatagramMuxer struct {
|
||||||
session quic.Connection
|
session quic.Connection
|
||||||
logger *zerolog.Logger
|
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()
|
logger := log.With().Uint8("datagramVersion", 1).Logger()
|
||||||
return &DatagramMuxer{
|
return &DatagramMuxer{
|
||||||
session: quicSession,
|
session: quicSession,
|
||||||
|
@ -47,13 +44,13 @@ func (dm *DatagramMuxer) mtu() int {
|
||||||
return maxDatagramPayloadSize
|
return maxDatagramPayloadSize
|
||||||
}
|
}
|
||||||
|
|
||||||
func (dm *DatagramMuxer) MuxSession(sessionID uuid.UUID, payload []byte) error {
|
func (dm *DatagramMuxer) SendToSession(session *packet.Session) error {
|
||||||
if len(payload) > dm.mtu() {
|
if len(session.Payload) > dm.mtu() {
|
||||||
// TODO: TUN-5302 return ICMP packet too big message
|
// TODO: TUN-5302 return ICMP packet too big message
|
||||||
// drop packet for now, eventually reply with ICMP for PMTUD
|
// 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())
|
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 {
|
if err != nil {
|
||||||
return errors.Wrap(err, "Failed to suffix session ID to datagram, it will be dropped")
|
return errors.Wrap(err, "Failed to suffix session ID to datagram, it will be dropped")
|
||||||
}
|
}
|
||||||
|
@ -86,7 +83,7 @@ func (dm *DatagramMuxer) demux(ctx context.Context, msg []byte) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
sessionDatagram := SessionDatagram{
|
sessionDatagram := packet.Session{
|
||||||
ID: sessionID,
|
ID: sessionID,
|
||||||
Payload: payload,
|
Payload: payload,
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
package quic
|
package quic
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"context"
|
"context"
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"crypto/rsa"
|
"crypto/rsa"
|
||||||
|
@ -18,6 +17,8 @@ import (
|
||||||
"github.com/rs/zerolog"
|
"github.com/rs/zerolog"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
"golang.org/x/sync/errgroup"
|
"golang.org/x/sync/errgroup"
|
||||||
|
|
||||||
|
"github.com/cloudflare/cloudflared/packet"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -57,7 +58,7 @@ func TestDatagram(t *testing.T) {
|
||||||
maxPayload := make([]byte, maxDatagramPayloadSize)
|
maxPayload := make([]byte, maxDatagramPayloadSize)
|
||||||
noPayloadSession := uuid.New()
|
noPayloadSession := uuid.New()
|
||||||
maxPayloadSession := uuid.New()
|
maxPayloadSession := uuid.New()
|
||||||
sessionToPayload := []*SessionDatagram{
|
sessionToPayload := []*packet.Session{
|
||||||
{
|
{
|
||||||
ID: noPayloadSession,
|
ID: noPayloadSession,
|
||||||
Payload: make([]byte, 0),
|
Payload: make([]byte, 0),
|
||||||
|
@ -75,7 +76,7 @@ func TestDatagram(t *testing.T) {
|
||||||
testDatagram(t, 2, sessionToPayload, flowPayloads)
|
testDatagram(t, 2, sessionToPayload, flowPayloads)
|
||||||
}
|
}
|
||||||
|
|
||||||
func testDatagram(t *testing.T, version uint8, sessionToPayloads []*SessionDatagram, packetPayloads [][]byte) {
|
func testDatagram(t *testing.T, version uint8, sessionToPayloads []*packet.Session, packetPayloads [][]byte) {
|
||||||
quicConfig := &quic.Config{
|
quicConfig := &quic.Config{
|
||||||
KeepAlivePeriod: 5 * time.Millisecond,
|
KeepAlivePeriod: 5 * time.Millisecond,
|
||||||
EnableDatagrams: true,
|
EnableDatagrams: true,
|
||||||
|
@ -95,7 +96,7 @@ func testDatagram(t *testing.T, version uint8, sessionToPayloads []*SessionDatag
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
sessionDemuxChan := make(chan *SessionDatagram, 16)
|
sessionDemuxChan := make(chan *packet.Session, 16)
|
||||||
|
|
||||||
switch version {
|
switch version {
|
||||||
case 1:
|
case 1:
|
||||||
|
@ -151,11 +152,14 @@ func testDatagram(t *testing.T, version uint8, sessionToPayloads []*SessionDatag
|
||||||
return fmt.Errorf("unknown datagram version %d", version)
|
return fmt.Errorf("unknown datagram version %d", version)
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, sessionDatagram := range sessionToPayloads {
|
for _, session := range sessionToPayloads {
|
||||||
require.NoError(t, muxer.MuxSession(sessionDatagram.ID, sessionDatagram.Payload))
|
require.NoError(t, muxer.SendToSession(session))
|
||||||
}
|
}
|
||||||
// Payload larger than transport MTU, should not be sent
|
// 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
|
// Wait for edge to finish receiving the messages
|
||||||
time.Sleep(time.Millisecond * 100)
|
time.Sleep(time.Millisecond * 100)
|
||||||
|
@ -198,35 +202,3 @@ func generateTLSConfig() *tls.Config {
|
||||||
NextProtos: []string{"argotunnel"},
|
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
|
|
||||||
}
|
|
||||||
|
|
|
@ -4,10 +4,11 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/google/uuid"
|
|
||||||
"github.com/lucas-clemente/quic-go"
|
"github.com/lucas-clemente/quic-go"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/rs/zerolog"
|
"github.com/rs/zerolog"
|
||||||
|
|
||||||
|
"github.com/cloudflare/cloudflared/packet"
|
||||||
)
|
)
|
||||||
|
|
||||||
type datagramV2Type byte
|
type datagramV2Type byte
|
||||||
|
@ -33,14 +34,14 @@ func (dm *DatagramMuxerV2) mtu() int {
|
||||||
type DatagramMuxerV2 struct {
|
type DatagramMuxerV2 struct {
|
||||||
session quic.Connection
|
session quic.Connection
|
||||||
logger *zerolog.Logger
|
logger *zerolog.Logger
|
||||||
sessionDemuxChan chan<- *SessionDatagram
|
sessionDemuxChan chan<- *packet.Session
|
||||||
packetDemuxChan chan<- []byte
|
packetDemuxChan chan<- []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewDatagramMuxerV2(
|
func NewDatagramMuxerV2(
|
||||||
quicSession quic.Connection,
|
quicSession quic.Connection,
|
||||||
log *zerolog.Logger,
|
log *zerolog.Logger,
|
||||||
sessionDemuxChan chan<- *SessionDatagram,
|
sessionDemuxChan chan<- *packet.Session,
|
||||||
packetDemuxChan chan<- []byte) *DatagramMuxerV2 {
|
packetDemuxChan chan<- []byte) *DatagramMuxerV2 {
|
||||||
logger := log.With().Uint8("datagramVersion", 2).Logger()
|
logger := log.With().Uint8("datagramVersion", 2).Logger()
|
||||||
return &DatagramMuxerV2{
|
return &DatagramMuxerV2{
|
||||||
|
@ -53,12 +54,12 @@ func NewDatagramMuxerV2(
|
||||||
|
|
||||||
// MuxSession suffix the session ID and datagram version to the payload so the other end of the QUIC connection can
|
// MuxSession 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
|
// demultiplex the payload from multiple datagram sessions
|
||||||
func (dm *DatagramMuxerV2) MuxSession(sessionID uuid.UUID, payload []byte) error {
|
func (dm *DatagramMuxerV2) SendToSession(session *packet.Session) error {
|
||||||
if len(payload) > dm.mtu() {
|
if len(session.Payload) > dm.mtu() {
|
||||||
// TODO: TUN-5302 return ICMP packet too big message
|
// 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())
|
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 {
|
if err != nil {
|
||||||
return errors.Wrap(err, "Failed to suffix session ID to datagram, it will be dropped")
|
return errors.Wrap(err, "Failed to suffix session ID to datagram, it will be dropped")
|
||||||
}
|
}
|
||||||
|
@ -113,7 +114,7 @@ func (dm *DatagramMuxerV2) demux(ctx context.Context, msgWithType []byte) error
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
sessionDatagram := SessionDatagram{
|
sessionDatagram := packet.Session{
|
||||||
ID: sessionID,
|
ID: sessionID,
|
||||||
Payload: payload,
|
Payload: payload,
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,38 @@
|
||||||
|
# Compiled Object files, Static and Dynamic libs (Shared Objects)
|
||||||
|
*.o
|
||||||
|
*.a
|
||||||
|
*.so
|
||||||
|
|
||||||
|
# Folders
|
||||||
|
_obj
|
||||||
|
_test
|
||||||
|
|
||||||
|
# Architecture specific extensions/prefixes
|
||||||
|
*.[568vq]
|
||||||
|
[568vq].out
|
||||||
|
|
||||||
|
*.cgo1.go
|
||||||
|
*.cgo2.c
|
||||||
|
_cgo_defun.c
|
||||||
|
_cgo_gotypes.go
|
||||||
|
_cgo_export.*
|
||||||
|
|
||||||
|
_testmain.go
|
||||||
|
|
||||||
|
*.exe
|
||||||
|
#*
|
||||||
|
*~
|
||||||
|
|
||||||
|
# examples binaries
|
||||||
|
examples/synscan/synscan
|
||||||
|
examples/pfdump/pfdump
|
||||||
|
examples/pcapdump/pcapdump
|
||||||
|
examples/httpassembly/httpassembly
|
||||||
|
examples/statsassembly/statsassembly
|
||||||
|
examples/arpscan/arpscan
|
||||||
|
examples/bidirectional/bidirectional
|
||||||
|
examples/bytediff/bytediff
|
||||||
|
examples/reassemblydump/reassemblydump
|
||||||
|
layers/gen
|
||||||
|
macs/gen
|
||||||
|
pcap/pcap_tester
|
|
@ -0,0 +1,7 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
cd "$(dirname $0)"
|
||||||
|
if [ -n "$(go fmt ./...)" ]; then
|
||||||
|
echo "Go code is not formatted, run 'go fmt github.com/google/stenographer/...'" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
|
@ -0,0 +1,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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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>
|
|
@ -0,0 +1,215 @@
|
||||||
|
Contributing To gopacket
|
||||||
|
========================
|
||||||
|
|
||||||
|
So you've got some code and you'd like it to be part of gopacket... wonderful!
|
||||||
|
We're happy to accept contributions, whether they're fixes to old protocols, new
|
||||||
|
protocols entirely, or anything else you think would improve the gopacket
|
||||||
|
library. This document is designed to help you to do just that.
|
||||||
|
|
||||||
|
The first section deals with the plumbing: how to actually get a change
|
||||||
|
submitted.
|
||||||
|
|
||||||
|
The second section deals with coding style... Go is great in that it
|
||||||
|
has a uniform style implemented by 'go fmt', but there's still some decisions
|
||||||
|
we've made that go above and beyond, and if you follow them, they won't come up
|
||||||
|
in your code review.
|
||||||
|
|
||||||
|
The third section deals with some of the implementation decisions we've made,
|
||||||
|
which may help you to understand the current code and which we may ask you to
|
||||||
|
conform to (or provide compelling reasons for ignoring).
|
||||||
|
|
||||||
|
Overall, we hope this document will help you to understand our system and write
|
||||||
|
great code which fits in, and help us to turn around on your code review quickly
|
||||||
|
so the code can make it into the master branch as quickly as possible.
|
||||||
|
|
||||||
|
|
||||||
|
How To Submit Code
|
||||||
|
------------------
|
||||||
|
|
||||||
|
We use github.com's Pull Request feature to receive code contributions from
|
||||||
|
external contributors. See
|
||||||
|
https://help.github.com/articles/creating-a-pull-request/ for details on
|
||||||
|
how to create a request.
|
||||||
|
|
||||||
|
Also, there's a local script `gc` in the base directory of GoPacket that
|
||||||
|
runs a local set of checks, which should give you relatively high confidence
|
||||||
|
that your pull won't fail github pull checks.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
go get github.com/google/gopacket
|
||||||
|
cd $GOROOT/src/pkg/github.com/google/gopacket
|
||||||
|
git checkout -b <mynewfeature> # create a new branch to work from
|
||||||
|
... code code code ...
|
||||||
|
./gc # Run this to do local commits, it performs a number of checks
|
||||||
|
```
|
||||||
|
|
||||||
|
To sum up:
|
||||||
|
|
||||||
|
* DO
|
||||||
|
+ Pull down the latest version.
|
||||||
|
+ Make a feature-specific branch.
|
||||||
|
+ Code using the style and methods discussed in the rest of this document.
|
||||||
|
+ Use the ./gc command to do local commits or check correctness.
|
||||||
|
+ Push your new feature branch up to github.com, as a pull request.
|
||||||
|
+ Handle comments and requests from reviewers, pushing new commits up to
|
||||||
|
your feature branch as problems are addressed.
|
||||||
|
+ Put interesting comments and discussions into commit comments.
|
||||||
|
* DON'T
|
||||||
|
+ Push to someone else's branch without their permission.
|
||||||
|
|
||||||
|
|
||||||
|
Coding Style
|
||||||
|
------------
|
||||||
|
|
||||||
|
* Go code must be run through `go fmt`, `go vet`, and `golint`
|
||||||
|
* Follow http://golang.org/doc/effective_go.html as much as possible.
|
||||||
|
+ In particular, http://golang.org/doc/effective_go.html#mixed-caps. Enums
|
||||||
|
should be be CamelCase, with acronyms capitalized (TCPSourcePort, vs.
|
||||||
|
TcpSourcePort or TCP_SOURCE_PORT).
|
||||||
|
* Bonus points for giving enum types a String() field.
|
||||||
|
* Any exported types or functions should have commentary
|
||||||
|
(http://golang.org/doc/effective_go.html#commentary)
|
||||||
|
|
||||||
|
|
||||||
|
Coding Methods And Implementation Notes
|
||||||
|
---------------------------------------
|
||||||
|
|
||||||
|
### Error Handling
|
||||||
|
|
||||||
|
Many times, you'll be decoding a protocol and run across something bad, a packet
|
||||||
|
corruption or the like. How do you handle this? First off, ALWAYS report the
|
||||||
|
error. You can do this either by returning the error from the decode() function
|
||||||
|
(most common), or if you're up for it you can implement and add an ErrorLayer
|
||||||
|
through the packet builder (the first method is a simple shortcut that does
|
||||||
|
exactly this, then stops any future decoding).
|
||||||
|
|
||||||
|
Often, you'll already have decode some part of your protocol by the time you hit
|
||||||
|
your error. Use your own discretion to determine whether the stuff you've
|
||||||
|
already decoded should be returned to the caller or not:
|
||||||
|
|
||||||
|
```go
|
||||||
|
func decodeMyProtocol(data []byte, p gopacket.PacketBuilder) error {
|
||||||
|
prot := &MyProtocol{}
|
||||||
|
if len(data) < 10 {
|
||||||
|
// This error occurred before we did ANYTHING, so there's nothing in my
|
||||||
|
// protocol that the caller could possibly want. Just return the error.
|
||||||
|
return fmt.Errorf("Length %d less than 10", len(data))
|
||||||
|
}
|
||||||
|
prot.ImportantField1 = data[:5]
|
||||||
|
prot.ImportantField2 = data[5:10]
|
||||||
|
// At this point, we've already got enough information in 'prot' to
|
||||||
|
// warrant returning it to the caller, so we'll add it now.
|
||||||
|
p.AddLayer(prot)
|
||||||
|
if len(data) < 15 {
|
||||||
|
// We encountered an error later in the packet, but the caller already
|
||||||
|
// has the important info we've gleaned so far.
|
||||||
|
return fmt.Errorf("Length %d less than 15", len(data))
|
||||||
|
}
|
||||||
|
prot.ImportantField3 = data[10:15]
|
||||||
|
return nil // We've already added the layer, we can just return success.
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
In general, our code follows the approach of returning the first error it
|
||||||
|
encounters. In general, we don't trust any bytes after the first error we see.
|
||||||
|
|
||||||
|
### What Is A Layer?
|
||||||
|
|
||||||
|
The definition of a layer is up to the discretion of the coder. It should be
|
||||||
|
something important enough that it's actually useful to the caller (IE: every
|
||||||
|
TLV value should probably NOT be a layer). However, it can be more granular
|
||||||
|
than a single protocol... IPv6 and SCTP both implement many layers to handle the
|
||||||
|
various parts of the protocol. Use your best judgement, and prepare to defend
|
||||||
|
your decisions during code review. ;)
|
||||||
|
|
||||||
|
### Performance
|
||||||
|
|
||||||
|
We strive to make gopacket as fast as possible while still providing lots of
|
||||||
|
features. In general, this means:
|
||||||
|
|
||||||
|
* Focus performance tuning on common protocols (IP4/6, TCP, etc), and optimize
|
||||||
|
others on an as-needed basis (tons of MPLS on your network? Time to optimize
|
||||||
|
MPLS!)
|
||||||
|
* Use fast operations. See the toplevel benchmark_test for benchmarks of some
|
||||||
|
of Go's underlying features and types.
|
||||||
|
* Test your performance changes! You should use the ./gc script's --benchmark
|
||||||
|
flag to submit any performance-related changes. Use pcap/gopacket_benchmark
|
||||||
|
to test your change against a PCAP file based on your traffic patterns.
|
||||||
|
* Don't be TOO hacky. Sometimes, removing an unused struct from a field causes
|
||||||
|
a huge performance hit, due to the way that Go currently handles its segmented
|
||||||
|
stack... don't be afraid to clean it up anyway. We'll trust the Go compiler
|
||||||
|
to get good enough over time to handle this. Also, this type of
|
||||||
|
compiler-specific optimization is very fragile; someone adding a field to an
|
||||||
|
entirely different struct elsewhere in the codebase could reverse any gains
|
||||||
|
you might achieve by aligning your allocations.
|
||||||
|
* Try to minimize memory allocations. If possible, use []byte to reference
|
||||||
|
pieces of the input, instead of using string, which requires copying the bytes
|
||||||
|
into a new memory allocation.
|
||||||
|
* Think hard about what should be evaluated lazily vs. not. In general, a
|
||||||
|
layer's struct should almost exactly mirror the layer's frame. Anything
|
||||||
|
that's more interesting should be a function. This may not always be
|
||||||
|
possible, but it's a good rule of thumb.
|
||||||
|
* Don't fear micro-optimizations. With the above in mind, we welcome
|
||||||
|
micro-optimizations that we think will have positive/neutral impacts on the
|
||||||
|
majority of workloads. A prime example of this is pre-allocating certain
|
||||||
|
structs within a larger one:
|
||||||
|
|
||||||
|
```go
|
||||||
|
type MyProtocol struct {
|
||||||
|
// Most packets have 1-4 of VeryCommon, so we preallocate it here.
|
||||||
|
initialAllocation [4]uint32
|
||||||
|
VeryCommon []uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
func decodeMyProtocol(data []byte, p gopacket.PacketBuilder) error {
|
||||||
|
prot := &MyProtocol{}
|
||||||
|
prot.VeryCommon = proto.initialAllocation[:0]
|
||||||
|
for len(data) > 4 {
|
||||||
|
field := binary.BigEndian.Uint32(data[:4])
|
||||||
|
data = data[4:]
|
||||||
|
// Since we're using the underlying initialAllocation, we won't need to
|
||||||
|
// allocate new memory for the following append unless we more than 16
|
||||||
|
// bytes of data, which should be the uncommon case.
|
||||||
|
prot.VeryCommon = append(prot.VeryCommon, field)
|
||||||
|
}
|
||||||
|
p.AddLayer(prot)
|
||||||
|
if len(data) > 0 {
|
||||||
|
return fmt.Errorf("MyProtocol packet has %d bytes left after decoding", len(data))
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Slices And Data
|
||||||
|
|
||||||
|
If you're pulling a slice from the data you're decoding, don't copy it. Just
|
||||||
|
use the slice itself.
|
||||||
|
|
||||||
|
```go
|
||||||
|
type MyProtocol struct {
|
||||||
|
A, B net.IP
|
||||||
|
}
|
||||||
|
func decodeMyProtocol(data []byte, p gopacket.PacketBuilder) error {
|
||||||
|
p.AddLayer(&MyProtocol{
|
||||||
|
A: data[:4],
|
||||||
|
B: data[4:8],
|
||||||
|
})
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The caller has already agreed, by using this library, that they won't modify the
|
||||||
|
set of bytes they pass in to the decoder, or the library has already copied the
|
||||||
|
set of bytes to a read-only location. See DecodeOptions.NoCopy for more
|
||||||
|
information.
|
||||||
|
|
||||||
|
### Enums/Types
|
||||||
|
|
||||||
|
If a protocol has an integer field (uint8, uint16, etc) with a couple of known
|
||||||
|
values that mean something special, make it a type. This allows us to do really
|
||||||
|
nice things like adding a String() function to them, so we can more easily
|
||||||
|
display those to users. Check out layers/enums.go for one example, as well as
|
||||||
|
layers/icmp.go for layer-specific enums.
|
||||||
|
|
||||||
|
When naming things, try for descriptiveness over suscinctness. For example,
|
||||||
|
choose DNSResponseRecord over DNSRR.
|
|
@ -0,0 +1,28 @@
|
||||||
|
Copyright (c) 2012 Google, Inc. All rights reserved.
|
||||||
|
Copyright (c) 2009-2011 Andreas Krennmair. All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are
|
||||||
|
met:
|
||||||
|
|
||||||
|
* Redistributions of source code must retain the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer.
|
||||||
|
* Redistributions in binary form must reproduce the above
|
||||||
|
copyright notice, this list of conditions and the following disclaimer
|
||||||
|
in the documentation and/or other materials provided with the
|
||||||
|
distribution.
|
||||||
|
* Neither the name of Andreas Krennmair, Google, nor the names of its
|
||||||
|
contributors may be used to endorse or promote products derived from
|
||||||
|
this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
@ -0,0 +1,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).
|
|
@ -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
|
||||||
|
}
|
|
@ -0,0 +1,157 @@
|
||||||
|
// Copyright 2012 Google, Inc. All rights reserved.
|
||||||
|
//
|
||||||
|
// Use of this source code is governed by a BSD-style license
|
||||||
|
// that can be found in the LICENSE file in the root of the source
|
||||||
|
// tree.
|
||||||
|
|
||||||
|
package gopacket
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
// DecodeFeedback is used by DecodingLayer layers to provide decoding metadata.
|
||||||
|
type DecodeFeedback interface {
|
||||||
|
// SetTruncated should be called if during decoding you notice that a packet
|
||||||
|
// is shorter than internal layer variables (HeaderLength, or the like) say it
|
||||||
|
// should be. It sets packet.Metadata().Truncated.
|
||||||
|
SetTruncated()
|
||||||
|
}
|
||||||
|
|
||||||
|
type nilDecodeFeedback struct{}
|
||||||
|
|
||||||
|
func (nilDecodeFeedback) SetTruncated() {}
|
||||||
|
|
||||||
|
// NilDecodeFeedback implements DecodeFeedback by doing nothing.
|
||||||
|
var NilDecodeFeedback DecodeFeedback = nilDecodeFeedback{}
|
||||||
|
|
||||||
|
// PacketBuilder is used by layer decoders to store the layers they've decoded,
|
||||||
|
// and to defer future decoding via NextDecoder.
|
||||||
|
// Typically, the pattern for use is:
|
||||||
|
// func (m *myDecoder) Decode(data []byte, p PacketBuilder) error {
|
||||||
|
// if myLayer, err := myDecodingLogic(data); err != nil {
|
||||||
|
// return err
|
||||||
|
// } else {
|
||||||
|
// p.AddLayer(myLayer)
|
||||||
|
// }
|
||||||
|
// // maybe do this, if myLayer is a LinkLayer
|
||||||
|
// p.SetLinkLayer(myLayer)
|
||||||
|
// return p.NextDecoder(nextDecoder)
|
||||||
|
// }
|
||||||
|
type PacketBuilder interface {
|
||||||
|
DecodeFeedback
|
||||||
|
// AddLayer should be called by a decoder immediately upon successful
|
||||||
|
// decoding of a layer.
|
||||||
|
AddLayer(l Layer)
|
||||||
|
// The following functions set the various specific layers in the final
|
||||||
|
// packet. Note that if many layers call SetX, the first call is kept and all
|
||||||
|
// other calls are ignored.
|
||||||
|
SetLinkLayer(LinkLayer)
|
||||||
|
SetNetworkLayer(NetworkLayer)
|
||||||
|
SetTransportLayer(TransportLayer)
|
||||||
|
SetApplicationLayer(ApplicationLayer)
|
||||||
|
SetErrorLayer(ErrorLayer)
|
||||||
|
// NextDecoder should be called by a decoder when they're done decoding a
|
||||||
|
// packet layer but not done with decoding the entire packet. The next
|
||||||
|
// decoder will be called to decode the last AddLayer's LayerPayload.
|
||||||
|
// Because of this, NextDecoder must only be called once all other
|
||||||
|
// PacketBuilder calls have been made. Set*Layer and AddLayer calls after
|
||||||
|
// NextDecoder calls will behave incorrectly.
|
||||||
|
NextDecoder(next Decoder) error
|
||||||
|
// DumpPacketData is used solely for decoding. If you come across an error
|
||||||
|
// you need to diagnose while processing a packet, call this and your packet's
|
||||||
|
// data will be dumped to stderr so you can create a test. This should never
|
||||||
|
// be called from a production decoder.
|
||||||
|
DumpPacketData()
|
||||||
|
// DecodeOptions returns the decode options
|
||||||
|
DecodeOptions() *DecodeOptions
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decoder is an interface for logic to decode a packet layer. Users may
|
||||||
|
// implement a Decoder to handle their own strange packet types, or may use one
|
||||||
|
// of the many decoders available in the 'layers' subpackage to decode things
|
||||||
|
// for them.
|
||||||
|
type Decoder interface {
|
||||||
|
// Decode decodes the bytes of a packet, sending decoded values and other
|
||||||
|
// information to PacketBuilder, and returning an error if unsuccessful. See
|
||||||
|
// the PacketBuilder documentation for more details.
|
||||||
|
Decode([]byte, PacketBuilder) error
|
||||||
|
}
|
||||||
|
|
||||||
|
// DecodeFunc wraps a function to make it a Decoder.
|
||||||
|
type DecodeFunc func([]byte, PacketBuilder) error
|
||||||
|
|
||||||
|
// Decode implements Decoder by calling itself.
|
||||||
|
func (d DecodeFunc) Decode(data []byte, p PacketBuilder) error {
|
||||||
|
// function, call thyself.
|
||||||
|
return d(data, p)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DecodePayload is a Decoder that returns a Payload layer containing all
|
||||||
|
// remaining bytes.
|
||||||
|
var DecodePayload Decoder = DecodeFunc(decodePayload)
|
||||||
|
|
||||||
|
// DecodeUnknown is a Decoder that returns an Unknown layer containing all
|
||||||
|
// remaining bytes, useful if you run up against a layer that you're unable to
|
||||||
|
// decode yet. This layer is considered an ErrorLayer.
|
||||||
|
var DecodeUnknown Decoder = DecodeFunc(decodeUnknown)
|
||||||
|
|
||||||
|
// DecodeFragment is a Decoder that returns a Fragment layer containing all
|
||||||
|
// remaining bytes.
|
||||||
|
var DecodeFragment Decoder = DecodeFunc(decodeFragment)
|
||||||
|
|
||||||
|
// LayerTypeZero is an invalid layer type, but can be used to determine whether
|
||||||
|
// layer type has actually been set correctly.
|
||||||
|
var LayerTypeZero = RegisterLayerType(0, LayerTypeMetadata{Name: "Unknown", Decoder: DecodeUnknown})
|
||||||
|
|
||||||
|
// LayerTypeDecodeFailure is the layer type for the default error layer.
|
||||||
|
var LayerTypeDecodeFailure = RegisterLayerType(1, LayerTypeMetadata{Name: "DecodeFailure", Decoder: DecodeUnknown})
|
||||||
|
|
||||||
|
// LayerTypePayload is the layer type for a payload that we don't try to decode
|
||||||
|
// but treat as a success, IE: an application-level payload.
|
||||||
|
var LayerTypePayload = RegisterLayerType(2, LayerTypeMetadata{Name: "Payload", Decoder: DecodePayload})
|
||||||
|
|
||||||
|
// LayerTypeFragment is the layer type for a fragment of a layer transported
|
||||||
|
// by an underlying layer that supports fragmentation.
|
||||||
|
var LayerTypeFragment = RegisterLayerType(3, LayerTypeMetadata{Name: "Fragment", Decoder: DecodeFragment})
|
||||||
|
|
||||||
|
// DecodeFailure is a packet layer created if decoding of the packet data failed
|
||||||
|
// for some reason. It implements ErrorLayer. LayerContents will be the entire
|
||||||
|
// set of bytes that failed to parse, and Error will return the reason parsing
|
||||||
|
// failed.
|
||||||
|
type DecodeFailure struct {
|
||||||
|
data []byte
|
||||||
|
err error
|
||||||
|
stack []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error returns the error encountered during decoding.
|
||||||
|
func (d *DecodeFailure) Error() error { return d.err }
|
||||||
|
|
||||||
|
// LayerContents implements Layer.
|
||||||
|
func (d *DecodeFailure) LayerContents() []byte { return d.data }
|
||||||
|
|
||||||
|
// LayerPayload implements Layer.
|
||||||
|
func (d *DecodeFailure) LayerPayload() []byte { return nil }
|
||||||
|
|
||||||
|
// String implements fmt.Stringer.
|
||||||
|
func (d *DecodeFailure) String() string {
|
||||||
|
return "Packet decoding error: " + d.Error().Error()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dump implements Dumper.
|
||||||
|
func (d *DecodeFailure) Dump() (s string) {
|
||||||
|
if d.stack != nil {
|
||||||
|
s = string(d.stack)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// LayerType returns LayerTypeDecodeFailure
|
||||||
|
func (d *DecodeFailure) LayerType() LayerType { return LayerTypeDecodeFailure }
|
||||||
|
|
||||||
|
// decodeUnknown "decodes" unsupported data types by returning an error.
|
||||||
|
// This decoder will thus always return a DecodeFailure layer.
|
||||||
|
func decodeUnknown(data []byte, p PacketBuilder) error {
|
||||||
|
return errors.New("Layer type not currently supported")
|
||||||
|
}
|
|
@ -0,0 +1,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, ð, &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(ð)
|
||||||
|
// ... 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(ð)
|
||||||
|
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
|
|
@ -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)
|
|
@ -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
|
|
@ -0,0 +1,107 @@
|
||||||
|
// Copyright 2012 Google, Inc. All rights reserved.
|
||||||
|
//
|
||||||
|
// Use of this source code is governed by a BSD-style license
|
||||||
|
// that can be found in the LICENSE file in the root of the source
|
||||||
|
// tree.
|
||||||
|
|
||||||
|
package gopacket
|
||||||
|
|
||||||
|
// LayerClass is a set of LayerTypes, used for grabbing one of a number of
|
||||||
|
// different types from a packet.
|
||||||
|
type LayerClass interface {
|
||||||
|
// Contains returns true if the given layer type should be considered part
|
||||||
|
// of this layer class.
|
||||||
|
Contains(LayerType) bool
|
||||||
|
// LayerTypes returns the set of all layer types in this layer class.
|
||||||
|
// Note that this may not be a fast operation on all LayerClass
|
||||||
|
// implementations.
|
||||||
|
LayerTypes() []LayerType
|
||||||
|
}
|
||||||
|
|
||||||
|
// Contains implements LayerClass.
|
||||||
|
func (l LayerType) Contains(a LayerType) bool {
|
||||||
|
return l == a
|
||||||
|
}
|
||||||
|
|
||||||
|
// LayerTypes implements LayerClass.
|
||||||
|
func (l LayerType) LayerTypes() []LayerType {
|
||||||
|
return []LayerType{l}
|
||||||
|
}
|
||||||
|
|
||||||
|
// LayerClassSlice implements a LayerClass with a slice.
|
||||||
|
type LayerClassSlice []bool
|
||||||
|
|
||||||
|
// Contains returns true if the given layer type should be considered part
|
||||||
|
// of this layer class.
|
||||||
|
func (s LayerClassSlice) Contains(t LayerType) bool {
|
||||||
|
return int(t) < len(s) && s[t]
|
||||||
|
}
|
||||||
|
|
||||||
|
// LayerTypes returns all layer types in this LayerClassSlice.
|
||||||
|
// Because of LayerClassSlice's implementation, this could be quite slow.
|
||||||
|
func (s LayerClassSlice) LayerTypes() (all []LayerType) {
|
||||||
|
for i := 0; i < len(s); i++ {
|
||||||
|
if s[i] {
|
||||||
|
all = append(all, LayerType(i))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewLayerClassSlice creates a new LayerClassSlice by creating a slice of
|
||||||
|
// size max(types) and setting slice[t] to true for each type t. Note, if
|
||||||
|
// you implement your own LayerType and give it a high value, this WILL create
|
||||||
|
// a very large slice.
|
||||||
|
func NewLayerClassSlice(types []LayerType) LayerClassSlice {
|
||||||
|
var max LayerType
|
||||||
|
for _, typ := range types {
|
||||||
|
if typ > max {
|
||||||
|
max = typ
|
||||||
|
}
|
||||||
|
}
|
||||||
|
t := make([]bool, int(max+1))
|
||||||
|
for _, typ := range types {
|
||||||
|
t[typ] = true
|
||||||
|
}
|
||||||
|
return t
|
||||||
|
}
|
||||||
|
|
||||||
|
// LayerClassMap implements a LayerClass with a map.
|
||||||
|
type LayerClassMap map[LayerType]bool
|
||||||
|
|
||||||
|
// Contains returns true if the given layer type should be considered part
|
||||||
|
// of this layer class.
|
||||||
|
func (m LayerClassMap) Contains(t LayerType) bool {
|
||||||
|
return m[t]
|
||||||
|
}
|
||||||
|
|
||||||
|
// LayerTypes returns all layer types in this LayerClassMap.
|
||||||
|
func (m LayerClassMap) LayerTypes() (all []LayerType) {
|
||||||
|
for t := range m {
|
||||||
|
all = append(all, t)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewLayerClassMap creates a LayerClassMap and sets map[t] to true for each
|
||||||
|
// type in types.
|
||||||
|
func NewLayerClassMap(types []LayerType) LayerClassMap {
|
||||||
|
m := LayerClassMap{}
|
||||||
|
for _, typ := range types {
|
||||||
|
m[typ] = true
|
||||||
|
}
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewLayerClass creates a LayerClass, attempting to be smart about which type
|
||||||
|
// it creates based on which types are passed in.
|
||||||
|
func NewLayerClass(types []LayerType) LayerClass {
|
||||||
|
for _, typ := range types {
|
||||||
|
if typ > maxLayerType {
|
||||||
|
// NewLayerClassSlice could create a very large object, so instead create
|
||||||
|
// a map.
|
||||||
|
return NewLayerClassMap(types)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return NewLayerClassSlice(types)
|
||||||
|
}
|
|
@ -0,0 +1,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
|
|
@ -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)
|
||||||
|
}
|
|
@ -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)
|
||||||
|
}
|
|
@ -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)
|
||||||
|
}
|
|
@ -0,0 +1,52 @@
|
||||||
|
// Copyright 2012 Google, Inc. All rights reserved.
|
||||||
|
//
|
||||||
|
// Use of this source code is governed by a BSD-style license
|
||||||
|
// that can be found in the LICENSE file in the root of the source
|
||||||
|
// tree.
|
||||||
|
|
||||||
|
package layers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/google/gopacket"
|
||||||
|
)
|
||||||
|
|
||||||
|
// BaseLayer is a convenience struct which implements the LayerData and
|
||||||
|
// LayerPayload functions of the Layer interface.
|
||||||
|
type BaseLayer struct {
|
||||||
|
// Contents is the set of bytes that make up this layer. IE: for an
|
||||||
|
// Ethernet packet, this would be the set of bytes making up the
|
||||||
|
// Ethernet frame.
|
||||||
|
Contents []byte
|
||||||
|
// Payload is the set of bytes contained by (but not part of) this
|
||||||
|
// Layer. Again, to take Ethernet as an example, this would be the
|
||||||
|
// set of bytes encapsulated by the Ethernet protocol.
|
||||||
|
Payload []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
// LayerContents returns the bytes of the packet layer.
|
||||||
|
func (b *BaseLayer) LayerContents() []byte { return b.Contents }
|
||||||
|
|
||||||
|
// LayerPayload returns the bytes contained within the packet layer.
|
||||||
|
func (b *BaseLayer) LayerPayload() []byte { return b.Payload }
|
||||||
|
|
||||||
|
type layerDecodingLayer interface {
|
||||||
|
gopacket.Layer
|
||||||
|
DecodeFromBytes([]byte, gopacket.DecodeFeedback) error
|
||||||
|
NextLayerType() gopacket.LayerType
|
||||||
|
}
|
||||||
|
|
||||||
|
func decodingLayerDecoder(d layerDecodingLayer, data []byte, p gopacket.PacketBuilder) error {
|
||||||
|
err := d.DecodeFromBytes(data, p)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
p.AddLayer(d)
|
||||||
|
next := d.NextLayerType()
|
||||||
|
if next == gopacket.LayerTypeZero {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return p.NextDecoder(next)
|
||||||
|
}
|
||||||
|
|
||||||
|
// hacky way to zero out memory... there must be a better way?
|
||||||
|
var lotsOfZeros [1024]byte
|
|
@ -0,0 +1,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
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
|
@ -0,0 +1,109 @@
|
||||||
|
// Copyright 2012 Google, Inc. All rights reserved.
|
||||||
|
//
|
||||||
|
// Use of this source code is governed by a BSD-style license
|
||||||
|
// that can be found in the LICENSE file in the root of the source
|
||||||
|
// tree.
|
||||||
|
|
||||||
|
package layers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/binary"
|
||||||
|
"fmt"
|
||||||
|
"github.com/google/gopacket"
|
||||||
|
)
|
||||||
|
|
||||||
|
// EthernetCTPFunction is the function code used by the EthernetCTP protocol to identify each
|
||||||
|
// EthernetCTP layer.
|
||||||
|
type EthernetCTPFunction uint16
|
||||||
|
|
||||||
|
// EthernetCTPFunction values.
|
||||||
|
const (
|
||||||
|
EthernetCTPFunctionReply EthernetCTPFunction = 1
|
||||||
|
EthernetCTPFunctionForwardData EthernetCTPFunction = 2
|
||||||
|
)
|
||||||
|
|
||||||
|
// EthernetCTP implements the EthernetCTP protocol, see http://www.mit.edu/people/jhawk/ctp.html.
|
||||||
|
// We split EthernetCTP up into the top-level EthernetCTP layer, followed by zero or more
|
||||||
|
// EthernetCTPForwardData layers, followed by a final EthernetCTPReply layer.
|
||||||
|
type EthernetCTP struct {
|
||||||
|
BaseLayer
|
||||||
|
SkipCount uint16
|
||||||
|
}
|
||||||
|
|
||||||
|
// LayerType returns gopacket.LayerTypeEthernetCTP.
|
||||||
|
func (c *EthernetCTP) LayerType() gopacket.LayerType {
|
||||||
|
return LayerTypeEthernetCTP
|
||||||
|
}
|
||||||
|
|
||||||
|
// EthernetCTPForwardData is the ForwardData layer inside EthernetCTP. See EthernetCTP's docs for more
|
||||||
|
// details.
|
||||||
|
type EthernetCTPForwardData struct {
|
||||||
|
BaseLayer
|
||||||
|
Function EthernetCTPFunction
|
||||||
|
ForwardAddress []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
// LayerType returns gopacket.LayerTypeEthernetCTPForwardData.
|
||||||
|
func (c *EthernetCTPForwardData) LayerType() gopacket.LayerType {
|
||||||
|
return LayerTypeEthernetCTPForwardData
|
||||||
|
}
|
||||||
|
|
||||||
|
// ForwardEndpoint returns the EthernetCTPForwardData ForwardAddress as an endpoint.
|
||||||
|
func (c *EthernetCTPForwardData) ForwardEndpoint() gopacket.Endpoint {
|
||||||
|
return gopacket.NewEndpoint(EndpointMAC, c.ForwardAddress)
|
||||||
|
}
|
||||||
|
|
||||||
|
// EthernetCTPReply is the Reply layer inside EthernetCTP. See EthernetCTP's docs for more details.
|
||||||
|
type EthernetCTPReply struct {
|
||||||
|
BaseLayer
|
||||||
|
Function EthernetCTPFunction
|
||||||
|
ReceiptNumber uint16
|
||||||
|
Data []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
// LayerType returns gopacket.LayerTypeEthernetCTPReply.
|
||||||
|
func (c *EthernetCTPReply) LayerType() gopacket.LayerType {
|
||||||
|
return LayerTypeEthernetCTPReply
|
||||||
|
}
|
||||||
|
|
||||||
|
// Payload returns the EthernetCTP reply's Data bytes.
|
||||||
|
func (c *EthernetCTPReply) Payload() []byte { return c.Data }
|
||||||
|
|
||||||
|
func decodeEthernetCTP(data []byte, p gopacket.PacketBuilder) error {
|
||||||
|
c := &EthernetCTP{
|
||||||
|
SkipCount: binary.LittleEndian.Uint16(data[:2]),
|
||||||
|
BaseLayer: BaseLayer{data[:2], data[2:]},
|
||||||
|
}
|
||||||
|
if c.SkipCount%2 != 0 {
|
||||||
|
return fmt.Errorf("EthernetCTP skip count is odd: %d", c.SkipCount)
|
||||||
|
}
|
||||||
|
p.AddLayer(c)
|
||||||
|
return p.NextDecoder(gopacket.DecodeFunc(decodeEthernetCTPFromFunctionType))
|
||||||
|
}
|
||||||
|
|
||||||
|
// decodeEthernetCTPFromFunctionType reads in the first 2 bytes to determine the EthernetCTP
|
||||||
|
// layer type to decode next, then decodes based on that.
|
||||||
|
func decodeEthernetCTPFromFunctionType(data []byte, p gopacket.PacketBuilder) error {
|
||||||
|
function := EthernetCTPFunction(binary.LittleEndian.Uint16(data[:2]))
|
||||||
|
switch function {
|
||||||
|
case EthernetCTPFunctionReply:
|
||||||
|
reply := &EthernetCTPReply{
|
||||||
|
Function: function,
|
||||||
|
ReceiptNumber: binary.LittleEndian.Uint16(data[2:4]),
|
||||||
|
Data: data[4:],
|
||||||
|
BaseLayer: BaseLayer{data, nil},
|
||||||
|
}
|
||||||
|
p.AddLayer(reply)
|
||||||
|
p.SetApplicationLayer(reply)
|
||||||
|
return nil
|
||||||
|
case EthernetCTPFunctionForwardData:
|
||||||
|
forward := &EthernetCTPForwardData{
|
||||||
|
Function: function,
|
||||||
|
ForwardAddress: data[2:8],
|
||||||
|
BaseLayer: BaseLayer{data[:8], data[8:]},
|
||||||
|
}
|
||||||
|
p.AddLayer(forward)
|
||||||
|
return p.NextDecoder(gopacket.DecodeFunc(decodeEthernetCTPFromFunctionType))
|
||||||
|
}
|
||||||
|
return fmt.Errorf("Unknown EthernetCTP function type %v", function)
|
||||||
|
}
|
|
@ -0,0 +1,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")
|
||||||
|
)
|
|
@ -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
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,61 @@
|
||||||
|
// Copyright 2012 Google, Inc. All rights reserved.
|
||||||
|
//
|
||||||
|
// Use of this source code is governed by a BSD-style license
|
||||||
|
// that can be found in the LICENSE file in the root of the source
|
||||||
|
// tree.
|
||||||
|
|
||||||
|
/*
|
||||||
|
Package layers provides decoding layers for many common protocols.
|
||||||
|
|
||||||
|
The layers package contains decode implementations for a number of different
|
||||||
|
types of packet layers. Users of gopacket will almost always want to also use
|
||||||
|
layers to actually decode packet data into useful pieces. To see the set of
|
||||||
|
protocols that gopacket/layers is currently able to decode,
|
||||||
|
look at the set of LayerTypes defined in the Variables sections. The
|
||||||
|
layers package also defines endpoints for many of the common packet layers
|
||||||
|
that have source/destination addresses associated with them, for example IPv4/6
|
||||||
|
(IPs) and TCP/UDP (ports).
|
||||||
|
Finally, layers contains a number of useful enumerations (IPProtocol,
|
||||||
|
EthernetType, LinkType, PPPType, etc...). Many of these implement the
|
||||||
|
gopacket.Decoder interface, so they can be passed into gopacket as decoders.
|
||||||
|
|
||||||
|
Most common protocol layers are named using acronyms or other industry-common
|
||||||
|
names (IPv4, TCP, PPP). Some of the less common ones have their names expanded
|
||||||
|
(CiscoDiscoveryProtocol).
|
||||||
|
For certain protocols, sub-parts of the protocol are split out into their own
|
||||||
|
layers (SCTP, for example). This is done mostly in cases where portions of the
|
||||||
|
protocol may fulfill the capabilities of interesting layers (SCTPData implements
|
||||||
|
ApplicationLayer, while base SCTP implements TransportLayer), or possibly
|
||||||
|
because splitting a protocol into a few layers makes decoding easier.
|
||||||
|
|
||||||
|
This package is meant to be used with its parent,
|
||||||
|
http://github.com/google/gopacket.
|
||||||
|
|
||||||
|
Port Types
|
||||||
|
|
||||||
|
Instead of using raw uint16 or uint8 values for ports, we use a different port
|
||||||
|
type for every protocol, for example TCPPort and UDPPort. This allows us to
|
||||||
|
override string behavior for each port, which we do by setting up port name
|
||||||
|
maps (TCPPortNames, UDPPortNames, etc...). Well-known ports are annotated with
|
||||||
|
their protocol names, and their String function displays these names:
|
||||||
|
|
||||||
|
p := TCPPort(80)
|
||||||
|
fmt.Printf("Number: %d String: %v", p, p)
|
||||||
|
// Prints: "Number: 80 String: 80(http)"
|
||||||
|
|
||||||
|
Modifying Decode Behavior
|
||||||
|
|
||||||
|
layers links together decoding through its enumerations. For example, after
|
||||||
|
decoding layer type Ethernet, it uses Ethernet.EthernetType as its next decoder.
|
||||||
|
All enumerations that act as decoders, like EthernetType, can be modified by
|
||||||
|
users depending on their preferences. For example, if you have a spiffy new
|
||||||
|
IPv4 decoder that works way better than the one built into layers, you can do
|
||||||
|
this:
|
||||||
|
|
||||||
|
var mySpiffyIPv4Decoder gopacket.Decoder = ...
|
||||||
|
layers.EthernetTypeMetadata[EthernetTypeIPv4].DecodeWith = mySpiffyIPv4Decoder
|
||||||
|
|
||||||
|
This will make all future ethernet packets use your new decoder to decode IPv4
|
||||||
|
packets, instead of the built-in decoder used by gopacket.
|
||||||
|
*/
|
||||||
|
package layers
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,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
|
||||||
|
}
|
|
@ -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)
|
||||||
|
}
|
|
@ -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)
|
||||||
|
}
|
|
@ -0,0 +1,97 @@
|
||||||
|
// Copyright 2012 Google, Inc. All rights reserved.
|
||||||
|
//
|
||||||
|
// Use of this source code is governed by a BSD-style license
|
||||||
|
// that can be found in the LICENSE file in the root of the source
|
||||||
|
// tree.
|
||||||
|
|
||||||
|
package layers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/binary"
|
||||||
|
"github.com/google/gopacket"
|
||||||
|
"net"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// We use two different endpoint types for IPv4 vs IPv6 addresses, so that
|
||||||
|
// ordering with endpointA.LessThan(endpointB) sanely groups all IPv4
|
||||||
|
// addresses and all IPv6 addresses, such that IPv6 > IPv4 for all addresses.
|
||||||
|
EndpointIPv4 = gopacket.RegisterEndpointType(1, gopacket.EndpointTypeMetadata{Name: "IPv4", Formatter: func(b []byte) string {
|
||||||
|
return net.IP(b).String()
|
||||||
|
}})
|
||||||
|
EndpointIPv6 = gopacket.RegisterEndpointType(2, gopacket.EndpointTypeMetadata{Name: "IPv6", Formatter: func(b []byte) string {
|
||||||
|
return net.IP(b).String()
|
||||||
|
}})
|
||||||
|
|
||||||
|
EndpointMAC = gopacket.RegisterEndpointType(3, gopacket.EndpointTypeMetadata{Name: "MAC", Formatter: func(b []byte) string {
|
||||||
|
return net.HardwareAddr(b).String()
|
||||||
|
}})
|
||||||
|
EndpointTCPPort = gopacket.RegisterEndpointType(4, gopacket.EndpointTypeMetadata{Name: "TCP", Formatter: func(b []byte) string {
|
||||||
|
return strconv.Itoa(int(binary.BigEndian.Uint16(b)))
|
||||||
|
}})
|
||||||
|
EndpointUDPPort = gopacket.RegisterEndpointType(5, gopacket.EndpointTypeMetadata{Name: "UDP", Formatter: func(b []byte) string {
|
||||||
|
return strconv.Itoa(int(binary.BigEndian.Uint16(b)))
|
||||||
|
}})
|
||||||
|
EndpointSCTPPort = gopacket.RegisterEndpointType(6, gopacket.EndpointTypeMetadata{Name: "SCTP", Formatter: func(b []byte) string {
|
||||||
|
return strconv.Itoa(int(binary.BigEndian.Uint16(b)))
|
||||||
|
}})
|
||||||
|
EndpointRUDPPort = gopacket.RegisterEndpointType(7, gopacket.EndpointTypeMetadata{Name: "RUDP", Formatter: func(b []byte) string {
|
||||||
|
return strconv.Itoa(int(b[0]))
|
||||||
|
}})
|
||||||
|
EndpointUDPLitePort = gopacket.RegisterEndpointType(8, gopacket.EndpointTypeMetadata{Name: "UDPLite", Formatter: func(b []byte) string {
|
||||||
|
return strconv.Itoa(int(binary.BigEndian.Uint16(b)))
|
||||||
|
}})
|
||||||
|
EndpointPPP = gopacket.RegisterEndpointType(9, gopacket.EndpointTypeMetadata{Name: "PPP", Formatter: func([]byte) string {
|
||||||
|
return "point"
|
||||||
|
}})
|
||||||
|
)
|
||||||
|
|
||||||
|
// NewIPEndpoint creates a new IP (v4 or v6) endpoint from a net.IP address.
|
||||||
|
// It returns gopacket.InvalidEndpoint if the IP address is invalid.
|
||||||
|
func NewIPEndpoint(a net.IP) gopacket.Endpoint {
|
||||||
|
ipv4 := a.To4()
|
||||||
|
if ipv4 != nil {
|
||||||
|
return gopacket.NewEndpoint(EndpointIPv4, []byte(ipv4))
|
||||||
|
}
|
||||||
|
|
||||||
|
ipv6 := a.To16()
|
||||||
|
if ipv6 != nil {
|
||||||
|
return gopacket.NewEndpoint(EndpointIPv6, []byte(ipv6))
|
||||||
|
}
|
||||||
|
|
||||||
|
return gopacket.InvalidEndpoint
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMACEndpoint returns a new MAC address endpoint.
|
||||||
|
func NewMACEndpoint(a net.HardwareAddr) gopacket.Endpoint {
|
||||||
|
return gopacket.NewEndpoint(EndpointMAC, []byte(a))
|
||||||
|
}
|
||||||
|
func newPortEndpoint(t gopacket.EndpointType, p uint16) gopacket.Endpoint {
|
||||||
|
return gopacket.NewEndpoint(t, []byte{byte(p >> 8), byte(p)})
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewTCPPortEndpoint returns an endpoint based on a TCP port.
|
||||||
|
func NewTCPPortEndpoint(p TCPPort) gopacket.Endpoint {
|
||||||
|
return newPortEndpoint(EndpointTCPPort, uint16(p))
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewUDPPortEndpoint returns an endpoint based on a UDP port.
|
||||||
|
func NewUDPPortEndpoint(p UDPPort) gopacket.Endpoint {
|
||||||
|
return newPortEndpoint(EndpointUDPPort, uint16(p))
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewSCTPPortEndpoint returns an endpoint based on a SCTP port.
|
||||||
|
func NewSCTPPortEndpoint(p SCTPPort) gopacket.Endpoint {
|
||||||
|
return newPortEndpoint(EndpointSCTPPort, uint16(p))
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewRUDPPortEndpoint returns an endpoint based on a RUDP port.
|
||||||
|
func NewRUDPPortEndpoint(p RUDPPort) gopacket.Endpoint {
|
||||||
|
return gopacket.NewEndpoint(EndpointRUDPPort, []byte{byte(p)})
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewUDPLitePortEndpoint returns an endpoint based on a UDPLite port.
|
||||||
|
func NewUDPLitePortEndpoint(p UDPLitePort) gopacket.Endpoint {
|
||||||
|
return newPortEndpoint(EndpointUDPLitePort, uint16(p))
|
||||||
|
}
|
|
@ -0,0 +1,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}
|
||||||
|
}
|
|
@ -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",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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)
|
||||||
|
}
|
|
@ -0,0 +1,45 @@
|
||||||
|
// Copyright 2012 Google, Inc. All rights reserved.
|
||||||
|
//
|
||||||
|
// Use of this source code is governed by a BSD-style license
|
||||||
|
// that can be found in the LICENSE file in the root of the source
|
||||||
|
// tree.
|
||||||
|
|
||||||
|
package layers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/binary"
|
||||||
|
"github.com/google/gopacket"
|
||||||
|
)
|
||||||
|
|
||||||
|
// EtherIP is the struct for storing RFC 3378 EtherIP packet headers.
|
||||||
|
type EtherIP struct {
|
||||||
|
BaseLayer
|
||||||
|
Version uint8
|
||||||
|
Reserved uint16
|
||||||
|
}
|
||||||
|
|
||||||
|
// LayerType returns gopacket.LayerTypeEtherIP.
|
||||||
|
func (e *EtherIP) LayerType() gopacket.LayerType { return LayerTypeEtherIP }
|
||||||
|
|
||||||
|
// DecodeFromBytes decodes the given bytes into this layer.
|
||||||
|
func (e *EtherIP) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
|
||||||
|
e.Version = data[0] >> 4
|
||||||
|
e.Reserved = binary.BigEndian.Uint16(data[:2]) & 0x0fff
|
||||||
|
e.BaseLayer = BaseLayer{data[:2], data[2:]}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CanDecode returns the set of layer types that this DecodingLayer can decode.
|
||||||
|
func (e *EtherIP) CanDecode() gopacket.LayerClass {
|
||||||
|
return LayerTypeEtherIP
|
||||||
|
}
|
||||||
|
|
||||||
|
// NextLayerType returns the layer type contained by this DecodingLayer.
|
||||||
|
func (e *EtherIP) NextLayerType() gopacket.LayerType {
|
||||||
|
return LayerTypeEthernet
|
||||||
|
}
|
||||||
|
|
||||||
|
func decodeEtherIP(data []byte, p gopacket.PacketBuilder) error {
|
||||||
|
e := &EtherIP{}
|
||||||
|
return decodingLayerDecoder(e, data, p)
|
||||||
|
}
|
|
@ -0,0 +1,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)
|
||||||
|
}
|
|
@ -0,0 +1,41 @@
|
||||||
|
// Copyright 2012 Google, Inc. All rights reserved.
|
||||||
|
//
|
||||||
|
// Use of this source code is governed by a BSD-style license
|
||||||
|
// that can be found in the LICENSE file in the root of the source
|
||||||
|
// tree.
|
||||||
|
|
||||||
|
package layers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/google/gopacket"
|
||||||
|
"net"
|
||||||
|
)
|
||||||
|
|
||||||
|
// FDDI contains the header for FDDI frames.
|
||||||
|
type FDDI struct {
|
||||||
|
BaseLayer
|
||||||
|
FrameControl FDDIFrameControl
|
||||||
|
Priority uint8
|
||||||
|
SrcMAC, DstMAC net.HardwareAddr
|
||||||
|
}
|
||||||
|
|
||||||
|
// LayerType returns LayerTypeFDDI.
|
||||||
|
func (f *FDDI) LayerType() gopacket.LayerType { return LayerTypeFDDI }
|
||||||
|
|
||||||
|
// LinkFlow returns a new flow of type EndpointMAC.
|
||||||
|
func (f *FDDI) LinkFlow() gopacket.Flow {
|
||||||
|
return gopacket.NewFlow(EndpointMAC, f.SrcMAC, f.DstMAC)
|
||||||
|
}
|
||||||
|
|
||||||
|
func decodeFDDI(data []byte, p gopacket.PacketBuilder) error {
|
||||||
|
f := &FDDI{
|
||||||
|
FrameControl: FDDIFrameControl(data[0] & 0xF8),
|
||||||
|
Priority: data[0] & 0x07,
|
||||||
|
SrcMAC: net.HardwareAddr(data[1:7]),
|
||||||
|
DstMAC: net.HardwareAddr(data[7:13]),
|
||||||
|
BaseLayer: BaseLayer{data[:13], data[13:]},
|
||||||
|
}
|
||||||
|
p.SetLinkLayer(f)
|
||||||
|
p.AddLayer(f)
|
||||||
|
return p.NextDecoder(f.FrameControl)
|
||||||
|
}
|
|
@ -0,0 +1,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
|
||||||
|
}
|
|
@ -0,0 +1,3 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
for i in *.go; do golint $i | grep -q . || echo $i; done > .linted
|
|
@ -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)
|
||||||
|
}
|
|
@ -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)
|
||||||
|
}
|
|
@ -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 := >Pv1U{}
|
||||||
|
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
|
@ -0,0 +1,267 @@
|
||||||
|
// Copyright 2012 Google, Inc. All rights reserved.
|
||||||
|
// Copyright 2009-2011 Andreas Krennmair. All rights reserved.
|
||||||
|
//
|
||||||
|
// Use of this source code is governed by a BSD-style license
|
||||||
|
// that can be found in the LICENSE file in the root of the source
|
||||||
|
// tree.
|
||||||
|
|
||||||
|
package layers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/binary"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
|
||||||
|
"github.com/google/gopacket"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
ICMPv4TypeEchoReply = 0
|
||||||
|
ICMPv4TypeDestinationUnreachable = 3
|
||||||
|
ICMPv4TypeSourceQuench = 4
|
||||||
|
ICMPv4TypeRedirect = 5
|
||||||
|
ICMPv4TypeEchoRequest = 8
|
||||||
|
ICMPv4TypeRouterAdvertisement = 9
|
||||||
|
ICMPv4TypeRouterSolicitation = 10
|
||||||
|
ICMPv4TypeTimeExceeded = 11
|
||||||
|
ICMPv4TypeParameterProblem = 12
|
||||||
|
ICMPv4TypeTimestampRequest = 13
|
||||||
|
ICMPv4TypeTimestampReply = 14
|
||||||
|
ICMPv4TypeInfoRequest = 15
|
||||||
|
ICMPv4TypeInfoReply = 16
|
||||||
|
ICMPv4TypeAddressMaskRequest = 17
|
||||||
|
ICMPv4TypeAddressMaskReply = 18
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// DestinationUnreachable
|
||||||
|
ICMPv4CodeNet = 0
|
||||||
|
ICMPv4CodeHost = 1
|
||||||
|
ICMPv4CodeProtocol = 2
|
||||||
|
ICMPv4CodePort = 3
|
||||||
|
ICMPv4CodeFragmentationNeeded = 4
|
||||||
|
ICMPv4CodeSourceRoutingFailed = 5
|
||||||
|
ICMPv4CodeNetUnknown = 6
|
||||||
|
ICMPv4CodeHostUnknown = 7
|
||||||
|
ICMPv4CodeSourceIsolated = 8
|
||||||
|
ICMPv4CodeNetAdminProhibited = 9
|
||||||
|
ICMPv4CodeHostAdminProhibited = 10
|
||||||
|
ICMPv4CodeNetTOS = 11
|
||||||
|
ICMPv4CodeHostTOS = 12
|
||||||
|
ICMPv4CodeCommAdminProhibited = 13
|
||||||
|
ICMPv4CodeHostPrecedence = 14
|
||||||
|
ICMPv4CodePrecedenceCutoff = 15
|
||||||
|
|
||||||
|
// TimeExceeded
|
||||||
|
ICMPv4CodeTTLExceeded = 0
|
||||||
|
ICMPv4CodeFragmentReassemblyTimeExceeded = 1
|
||||||
|
|
||||||
|
// ParameterProblem
|
||||||
|
ICMPv4CodePointerIndicatesError = 0
|
||||||
|
ICMPv4CodeMissingOption = 1
|
||||||
|
ICMPv4CodeBadLength = 2
|
||||||
|
|
||||||
|
// Redirect
|
||||||
|
// ICMPv4CodeNet = same as for DestinationUnreachable
|
||||||
|
// ICMPv4CodeHost = same as for DestinationUnreachable
|
||||||
|
ICMPv4CodeTOSNet = 2
|
||||||
|
ICMPv4CodeTOSHost = 3
|
||||||
|
)
|
||||||
|
|
||||||
|
type icmpv4TypeCodeInfoStruct struct {
|
||||||
|
typeStr string
|
||||||
|
codeStr *map[uint8]string
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
icmpv4TypeCodeInfo = map[uint8]icmpv4TypeCodeInfoStruct{
|
||||||
|
ICMPv4TypeDestinationUnreachable: icmpv4TypeCodeInfoStruct{
|
||||||
|
"DestinationUnreachable", &map[uint8]string{
|
||||||
|
ICMPv4CodeNet: "Net",
|
||||||
|
ICMPv4CodeHost: "Host",
|
||||||
|
ICMPv4CodeProtocol: "Protocol",
|
||||||
|
ICMPv4CodePort: "Port",
|
||||||
|
ICMPv4CodeFragmentationNeeded: "FragmentationNeeded",
|
||||||
|
ICMPv4CodeSourceRoutingFailed: "SourceRoutingFailed",
|
||||||
|
ICMPv4CodeNetUnknown: "NetUnknown",
|
||||||
|
ICMPv4CodeHostUnknown: "HostUnknown",
|
||||||
|
ICMPv4CodeSourceIsolated: "SourceIsolated",
|
||||||
|
ICMPv4CodeNetAdminProhibited: "NetAdminProhibited",
|
||||||
|
ICMPv4CodeHostAdminProhibited: "HostAdminProhibited",
|
||||||
|
ICMPv4CodeNetTOS: "NetTOS",
|
||||||
|
ICMPv4CodeHostTOS: "HostTOS",
|
||||||
|
ICMPv4CodeCommAdminProhibited: "CommAdminProhibited",
|
||||||
|
ICMPv4CodeHostPrecedence: "HostPrecedence",
|
||||||
|
ICMPv4CodePrecedenceCutoff: "PrecedenceCutoff",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ICMPv4TypeTimeExceeded: icmpv4TypeCodeInfoStruct{
|
||||||
|
"TimeExceeded", &map[uint8]string{
|
||||||
|
ICMPv4CodeTTLExceeded: "TTLExceeded",
|
||||||
|
ICMPv4CodeFragmentReassemblyTimeExceeded: "FragmentReassemblyTimeExceeded",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ICMPv4TypeParameterProblem: icmpv4TypeCodeInfoStruct{
|
||||||
|
"ParameterProblem", &map[uint8]string{
|
||||||
|
ICMPv4CodePointerIndicatesError: "PointerIndicatesError",
|
||||||
|
ICMPv4CodeMissingOption: "MissingOption",
|
||||||
|
ICMPv4CodeBadLength: "BadLength",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ICMPv4TypeSourceQuench: icmpv4TypeCodeInfoStruct{
|
||||||
|
"SourceQuench", nil,
|
||||||
|
},
|
||||||
|
ICMPv4TypeRedirect: icmpv4TypeCodeInfoStruct{
|
||||||
|
"Redirect", &map[uint8]string{
|
||||||
|
ICMPv4CodeNet: "Net",
|
||||||
|
ICMPv4CodeHost: "Host",
|
||||||
|
ICMPv4CodeTOSNet: "TOS+Net",
|
||||||
|
ICMPv4CodeTOSHost: "TOS+Host",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ICMPv4TypeEchoRequest: icmpv4TypeCodeInfoStruct{
|
||||||
|
"EchoRequest", nil,
|
||||||
|
},
|
||||||
|
ICMPv4TypeEchoReply: icmpv4TypeCodeInfoStruct{
|
||||||
|
"EchoReply", nil,
|
||||||
|
},
|
||||||
|
ICMPv4TypeTimestampRequest: icmpv4TypeCodeInfoStruct{
|
||||||
|
"TimestampRequest", nil,
|
||||||
|
},
|
||||||
|
ICMPv4TypeTimestampReply: icmpv4TypeCodeInfoStruct{
|
||||||
|
"TimestampReply", nil,
|
||||||
|
},
|
||||||
|
ICMPv4TypeInfoRequest: icmpv4TypeCodeInfoStruct{
|
||||||
|
"InfoRequest", nil,
|
||||||
|
},
|
||||||
|
ICMPv4TypeInfoReply: icmpv4TypeCodeInfoStruct{
|
||||||
|
"InfoReply", nil,
|
||||||
|
},
|
||||||
|
ICMPv4TypeRouterSolicitation: icmpv4TypeCodeInfoStruct{
|
||||||
|
"RouterSolicitation", nil,
|
||||||
|
},
|
||||||
|
ICMPv4TypeRouterAdvertisement: icmpv4TypeCodeInfoStruct{
|
||||||
|
"RouterAdvertisement", nil,
|
||||||
|
},
|
||||||
|
ICMPv4TypeAddressMaskRequest: icmpv4TypeCodeInfoStruct{
|
||||||
|
"AddressMaskRequest", nil,
|
||||||
|
},
|
||||||
|
ICMPv4TypeAddressMaskReply: icmpv4TypeCodeInfoStruct{
|
||||||
|
"AddressMaskReply", nil,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
type ICMPv4TypeCode uint16
|
||||||
|
|
||||||
|
// Type returns the ICMPv4 type field.
|
||||||
|
func (a ICMPv4TypeCode) Type() uint8 {
|
||||||
|
return uint8(a >> 8)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Code returns the ICMPv4 code field.
|
||||||
|
func (a ICMPv4TypeCode) Code() uint8 {
|
||||||
|
return uint8(a)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a ICMPv4TypeCode) String() string {
|
||||||
|
t, c := a.Type(), a.Code()
|
||||||
|
strInfo, ok := icmpv4TypeCodeInfo[t]
|
||||||
|
if !ok {
|
||||||
|
// Unknown ICMPv4 type field
|
||||||
|
return fmt.Sprintf("%d(%d)", t, c)
|
||||||
|
}
|
||||||
|
typeStr := strInfo.typeStr
|
||||||
|
if strInfo.codeStr == nil && c == 0 {
|
||||||
|
// The ICMPv4 type does not make use of the code field
|
||||||
|
return fmt.Sprintf("%s", strInfo.typeStr)
|
||||||
|
}
|
||||||
|
if strInfo.codeStr == nil && c != 0 {
|
||||||
|
// The ICMPv4 type does not make use of the code field, but it is present anyway
|
||||||
|
return fmt.Sprintf("%s(Code: %d)", typeStr, c)
|
||||||
|
}
|
||||||
|
codeStr, ok := (*strInfo.codeStr)[c]
|
||||||
|
if !ok {
|
||||||
|
// We don't know this ICMPv4 code; print the numerical value
|
||||||
|
return fmt.Sprintf("%s(Code: %d)", typeStr, c)
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("%s(%s)", typeStr, codeStr)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a ICMPv4TypeCode) GoString() string {
|
||||||
|
t := reflect.TypeOf(a)
|
||||||
|
return fmt.Sprintf("%s(%d, %d)", t.String(), a.Type(), a.Code())
|
||||||
|
}
|
||||||
|
|
||||||
|
// SerializeTo writes the ICMPv4TypeCode value to the 'bytes' buffer.
|
||||||
|
func (a ICMPv4TypeCode) SerializeTo(bytes []byte) {
|
||||||
|
binary.BigEndian.PutUint16(bytes, uint16(a))
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateICMPv4TypeCode is a convenience function to create an ICMPv4TypeCode
|
||||||
|
// gopacket type from the ICMPv4 type and code values.
|
||||||
|
func CreateICMPv4TypeCode(typ uint8, code uint8) ICMPv4TypeCode {
|
||||||
|
return ICMPv4TypeCode(binary.BigEndian.Uint16([]byte{typ, code}))
|
||||||
|
}
|
||||||
|
|
||||||
|
// ICMPv4 is the layer for IPv4 ICMP packet data.
|
||||||
|
type ICMPv4 struct {
|
||||||
|
BaseLayer
|
||||||
|
TypeCode ICMPv4TypeCode
|
||||||
|
Checksum uint16
|
||||||
|
Id uint16
|
||||||
|
Seq uint16
|
||||||
|
}
|
||||||
|
|
||||||
|
// LayerType returns LayerTypeICMPv4.
|
||||||
|
func (i *ICMPv4) LayerType() gopacket.LayerType { return LayerTypeICMPv4 }
|
||||||
|
|
||||||
|
// DecodeFromBytes decodes the given bytes into this layer.
|
||||||
|
func (i *ICMPv4) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
|
||||||
|
if len(data) < 8 {
|
||||||
|
df.SetTruncated()
|
||||||
|
return errors.New("ICMP layer less then 8 bytes for ICMPv4 packet")
|
||||||
|
}
|
||||||
|
i.TypeCode = CreateICMPv4TypeCode(data[0], data[1])
|
||||||
|
i.Checksum = binary.BigEndian.Uint16(data[2:4])
|
||||||
|
i.Id = binary.BigEndian.Uint16(data[4:6])
|
||||||
|
i.Seq = binary.BigEndian.Uint16(data[6:8])
|
||||||
|
i.BaseLayer = BaseLayer{data[:8], data[8:]}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SerializeTo writes the serialized form of this layer into the
|
||||||
|
// SerializationBuffer, implementing gopacket.SerializableLayer.
|
||||||
|
// See the docs for gopacket.SerializableLayer for more info.
|
||||||
|
func (i *ICMPv4) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
|
||||||
|
bytes, err := b.PrependBytes(8)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
i.TypeCode.SerializeTo(bytes)
|
||||||
|
binary.BigEndian.PutUint16(bytes[4:], i.Id)
|
||||||
|
binary.BigEndian.PutUint16(bytes[6:], i.Seq)
|
||||||
|
if opts.ComputeChecksums {
|
||||||
|
bytes[2] = 0
|
||||||
|
bytes[3] = 0
|
||||||
|
i.Checksum = tcpipChecksum(b.Bytes(), 0)
|
||||||
|
}
|
||||||
|
binary.BigEndian.PutUint16(bytes[2:], i.Checksum)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CanDecode returns the set of layer types that this DecodingLayer can decode.
|
||||||
|
func (i *ICMPv4) CanDecode() gopacket.LayerClass {
|
||||||
|
return LayerTypeICMPv4
|
||||||
|
}
|
||||||
|
|
||||||
|
// NextLayerType returns the layer type contained by this DecodingLayer.
|
||||||
|
func (i *ICMPv4) NextLayerType() gopacket.LayerType {
|
||||||
|
return gopacket.LayerTypePayload
|
||||||
|
}
|
||||||
|
|
||||||
|
func decodeICMPv4(data []byte, p gopacket.PacketBuilder) error {
|
||||||
|
i := &ICMPv4{}
|
||||||
|
return decodingLayerDecoder(i, data, p)
|
||||||
|
}
|
|
@ -0,0 +1,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)
|
||||||
|
}
|
|
@ -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)
|
||||||
|
}
|
|
@ -0,0 +1,355 @@
|
||||||
|
// Copyright 2012 Google, Inc. All rights reserved.
|
||||||
|
// Copyright 2009-2011 Andreas Krennmair. All rights reserved.
|
||||||
|
//
|
||||||
|
// Use of this source code is governed by a BSD-style license
|
||||||
|
// that can be found in the LICENSE file in the root of the source
|
||||||
|
// tree.
|
||||||
|
|
||||||
|
package layers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/binary"
|
||||||
|
"errors"
|
||||||
|
"net"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/google/gopacket"
|
||||||
|
)
|
||||||
|
|
||||||
|
type IGMPType uint8
|
||||||
|
|
||||||
|
const (
|
||||||
|
IGMPMembershipQuery IGMPType = 0x11 // General or group specific query
|
||||||
|
IGMPMembershipReportV1 IGMPType = 0x12 // Version 1 Membership Report
|
||||||
|
IGMPMembershipReportV2 IGMPType = 0x16 // Version 2 Membership Report
|
||||||
|
IGMPLeaveGroup IGMPType = 0x17 // Leave Group
|
||||||
|
IGMPMembershipReportV3 IGMPType = 0x22 // Version 3 Membership Report
|
||||||
|
)
|
||||||
|
|
||||||
|
// String conversions for IGMP message types
|
||||||
|
func (i IGMPType) String() string {
|
||||||
|
switch i {
|
||||||
|
case IGMPMembershipQuery:
|
||||||
|
return "IGMP Membership Query"
|
||||||
|
case IGMPMembershipReportV1:
|
||||||
|
return "IGMPv1 Membership Report"
|
||||||
|
case IGMPMembershipReportV2:
|
||||||
|
return "IGMPv2 Membership Report"
|
||||||
|
case IGMPMembershipReportV3:
|
||||||
|
return "IGMPv3 Membership Report"
|
||||||
|
case IGMPLeaveGroup:
|
||||||
|
return "Leave Group"
|
||||||
|
default:
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type IGMPv3GroupRecordType uint8
|
||||||
|
|
||||||
|
const (
|
||||||
|
IGMPIsIn IGMPv3GroupRecordType = 0x01 // Type MODE_IS_INCLUDE, source addresses x
|
||||||
|
IGMPIsEx IGMPv3GroupRecordType = 0x02 // Type MODE_IS_EXCLUDE, source addresses x
|
||||||
|
IGMPToIn IGMPv3GroupRecordType = 0x03 // Type CHANGE_TO_INCLUDE_MODE, source addresses x
|
||||||
|
IGMPToEx IGMPv3GroupRecordType = 0x04 // Type CHANGE_TO_EXCLUDE_MODE, source addresses x
|
||||||
|
IGMPAllow IGMPv3GroupRecordType = 0x05 // Type ALLOW_NEW_SOURCES, source addresses x
|
||||||
|
IGMPBlock IGMPv3GroupRecordType = 0x06 // Type BLOCK_OLD_SOURCES, source addresses x
|
||||||
|
)
|
||||||
|
|
||||||
|
func (i IGMPv3GroupRecordType) String() string {
|
||||||
|
switch i {
|
||||||
|
case IGMPIsIn:
|
||||||
|
return "MODE_IS_INCLUDE"
|
||||||
|
case IGMPIsEx:
|
||||||
|
return "MODE_IS_EXCLUDE"
|
||||||
|
case IGMPToIn:
|
||||||
|
return "CHANGE_TO_INCLUDE_MODE"
|
||||||
|
case IGMPToEx:
|
||||||
|
return "CHANGE_TO_EXCLUDE_MODE"
|
||||||
|
case IGMPAllow:
|
||||||
|
return "ALLOW_NEW_SOURCES"
|
||||||
|
case IGMPBlock:
|
||||||
|
return "BLOCK_OLD_SOURCES"
|
||||||
|
default:
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// IGMP represents an IGMPv3 message.
|
||||||
|
type IGMP struct {
|
||||||
|
BaseLayer
|
||||||
|
Type IGMPType
|
||||||
|
MaxResponseTime time.Duration
|
||||||
|
Checksum uint16
|
||||||
|
GroupAddress net.IP
|
||||||
|
SupressRouterProcessing bool
|
||||||
|
RobustnessValue uint8
|
||||||
|
IntervalTime time.Duration
|
||||||
|
SourceAddresses []net.IP
|
||||||
|
NumberOfGroupRecords uint16
|
||||||
|
NumberOfSources uint16
|
||||||
|
GroupRecords []IGMPv3GroupRecord
|
||||||
|
Version uint8 // IGMP protocol version
|
||||||
|
}
|
||||||
|
|
||||||
|
// IGMPv1or2 stores header details for an IGMPv1 or IGMPv2 packet.
|
||||||
|
//
|
||||||
|
// 0 1 2 3
|
||||||
|
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
||||||
|
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
|
// | Type | Max Resp Time | Checksum |
|
||||||
|
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
|
// | Group Address |
|
||||||
|
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
|
type IGMPv1or2 struct {
|
||||||
|
BaseLayer
|
||||||
|
Type IGMPType // IGMP message type
|
||||||
|
MaxResponseTime time.Duration // meaningful only in Membership Query messages
|
||||||
|
Checksum uint16 // 16-bit checksum of entire ip payload
|
||||||
|
GroupAddress net.IP // either 0 or an IP multicast address
|
||||||
|
Version uint8
|
||||||
|
}
|
||||||
|
|
||||||
|
// decodeResponse dissects IGMPv1 or IGMPv2 packet.
|
||||||
|
func (i *IGMPv1or2) decodeResponse(data []byte) error {
|
||||||
|
if len(data) < 8 {
|
||||||
|
return errors.New("IGMP packet too small")
|
||||||
|
}
|
||||||
|
|
||||||
|
i.MaxResponseTime = igmpTimeDecode(data[1])
|
||||||
|
i.Checksum = binary.BigEndian.Uint16(data[2:4])
|
||||||
|
i.GroupAddress = net.IP(data[4:8])
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 0 1 2 3
|
||||||
|
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
||||||
|
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
|
// | Type = 0x22 | Reserved | Checksum |
|
||||||
|
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
|
// | Reserved | Number of Group Records (M) |
|
||||||
|
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
|
// | |
|
||||||
|
// . Group Record [1] .
|
||||||
|
// | |
|
||||||
|
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
|
// | |
|
||||||
|
// . Group Record [2] .
|
||||||
|
// | |
|
||||||
|
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
|
// | |
|
||||||
|
// . Group Record [M] .
|
||||||
|
// | |
|
||||||
|
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
|
|
||||||
|
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
|
// | Record Type | Aux Data Len | Number of Sources (N) |
|
||||||
|
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
|
// | Multicast Address |
|
||||||
|
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
|
// | Source Address [1] |
|
||||||
|
// +- -+
|
||||||
|
// | Source Address [2] |
|
||||||
|
// +- -+
|
||||||
|
// | Source Address [N] |
|
||||||
|
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
|
// | |
|
||||||
|
// . Auxiliary Data .
|
||||||
|
// | |
|
||||||
|
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
|
|
||||||
|
// IGMPv3GroupRecord stores individual group records for a V3 Membership Report message.
|
||||||
|
type IGMPv3GroupRecord struct {
|
||||||
|
Type IGMPv3GroupRecordType
|
||||||
|
AuxDataLen uint8 // this should always be 0 as per IGMPv3 spec.
|
||||||
|
NumberOfSources uint16
|
||||||
|
MulticastAddress net.IP
|
||||||
|
SourceAddresses []net.IP
|
||||||
|
AuxData uint32 // NOT USED
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *IGMP) decodeIGMPv3MembershipReport(data []byte) error {
|
||||||
|
if len(data) < 8 {
|
||||||
|
return errors.New("IGMPv3 Membership Report too small #1")
|
||||||
|
}
|
||||||
|
|
||||||
|
i.Checksum = binary.BigEndian.Uint16(data[2:4])
|
||||||
|
i.NumberOfGroupRecords = binary.BigEndian.Uint16(data[6:8])
|
||||||
|
|
||||||
|
recordOffset := 8
|
||||||
|
for j := 0; j < int(i.NumberOfGroupRecords); j++ {
|
||||||
|
if len(data) < recordOffset+8 {
|
||||||
|
return errors.New("IGMPv3 Membership Report too small #2")
|
||||||
|
}
|
||||||
|
|
||||||
|
var gr IGMPv3GroupRecord
|
||||||
|
gr.Type = IGMPv3GroupRecordType(data[recordOffset])
|
||||||
|
gr.AuxDataLen = data[recordOffset+1]
|
||||||
|
gr.NumberOfSources = binary.BigEndian.Uint16(data[recordOffset+2 : recordOffset+4])
|
||||||
|
gr.MulticastAddress = net.IP(data[recordOffset+4 : recordOffset+8])
|
||||||
|
|
||||||
|
if len(data) < recordOffset+8+int(gr.NumberOfSources)*4 {
|
||||||
|
return errors.New("IGMPv3 Membership Report too small #3")
|
||||||
|
}
|
||||||
|
|
||||||
|
// append source address records.
|
||||||
|
for i := 0; i < int(gr.NumberOfSources); i++ {
|
||||||
|
sourceAddr := net.IP(data[recordOffset+8+i*4 : recordOffset+12+i*4])
|
||||||
|
gr.SourceAddresses = append(gr.SourceAddresses, sourceAddr)
|
||||||
|
}
|
||||||
|
|
||||||
|
i.GroupRecords = append(i.GroupRecords, gr)
|
||||||
|
recordOffset += 8 + 4*int(gr.NumberOfSources)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 0 1 2 3
|
||||||
|
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
||||||
|
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
|
// | Type = 0x11 | Max Resp Code | Checksum |
|
||||||
|
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
|
// | Group Address |
|
||||||
|
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
|
// | Resv |S| QRV | QQIC | Number of Sources (N) |
|
||||||
|
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
|
// | Source Address [1] |
|
||||||
|
// +- -+
|
||||||
|
// | Source Address [2] |
|
||||||
|
// +- . -+
|
||||||
|
// | Source Address [N] |
|
||||||
|
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
|
//
|
||||||
|
// decodeIGMPv3MembershipQuery parses the IGMPv3 message of type 0x11
|
||||||
|
func (i *IGMP) decodeIGMPv3MembershipQuery(data []byte) error {
|
||||||
|
if len(data) < 12 {
|
||||||
|
return errors.New("IGMPv3 Membership Query too small #1")
|
||||||
|
}
|
||||||
|
|
||||||
|
i.MaxResponseTime = igmpTimeDecode(data[1])
|
||||||
|
i.Checksum = binary.BigEndian.Uint16(data[2:4])
|
||||||
|
i.SupressRouterProcessing = data[8]&0x8 != 0
|
||||||
|
i.GroupAddress = net.IP(data[4:8])
|
||||||
|
i.RobustnessValue = data[8] & 0x7
|
||||||
|
i.IntervalTime = igmpTimeDecode(data[9])
|
||||||
|
i.NumberOfSources = binary.BigEndian.Uint16(data[10:12])
|
||||||
|
|
||||||
|
if len(data) < 12+int(i.NumberOfSources)*4 {
|
||||||
|
return errors.New("IGMPv3 Membership Query too small #2")
|
||||||
|
}
|
||||||
|
|
||||||
|
for j := 0; j < int(i.NumberOfSources); j++ {
|
||||||
|
i.SourceAddresses = append(i.SourceAddresses, net.IP(data[12+j*4:16+j*4]))
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// igmpTimeDecode decodes the duration created by the given byte, using the
|
||||||
|
// algorithm in http://www.rfc-base.org/txt/rfc-3376.txt section 4.1.1.
|
||||||
|
func igmpTimeDecode(t uint8) time.Duration {
|
||||||
|
if t&0x80 == 0 {
|
||||||
|
return time.Millisecond * 100 * time.Duration(t)
|
||||||
|
}
|
||||||
|
mant := (t & 0x70) >> 4
|
||||||
|
exp := t & 0x0F
|
||||||
|
return time.Millisecond * 100 * time.Duration((mant|0x10)<<(exp+3))
|
||||||
|
}
|
||||||
|
|
||||||
|
// LayerType returns LayerTypeIGMP for the V1,2,3 message protocol formats.
|
||||||
|
func (i *IGMP) LayerType() gopacket.LayerType { return LayerTypeIGMP }
|
||||||
|
func (i *IGMPv1or2) LayerType() gopacket.LayerType { return LayerTypeIGMP }
|
||||||
|
|
||||||
|
func (i *IGMPv1or2) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
|
||||||
|
if len(data) < 8 {
|
||||||
|
return errors.New("IGMP Packet too small")
|
||||||
|
}
|
||||||
|
|
||||||
|
i.Type = IGMPType(data[0])
|
||||||
|
i.MaxResponseTime = igmpTimeDecode(data[1])
|
||||||
|
i.Checksum = binary.BigEndian.Uint16(data[2:4])
|
||||||
|
i.GroupAddress = net.IP(data[4:8])
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *IGMPv1or2) NextLayerType() gopacket.LayerType {
|
||||||
|
return gopacket.LayerTypeZero
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *IGMPv1or2) CanDecode() gopacket.LayerClass {
|
||||||
|
return LayerTypeIGMP
|
||||||
|
}
|
||||||
|
|
||||||
|
// DecodeFromBytes decodes the given bytes into this layer.
|
||||||
|
func (i *IGMP) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
|
||||||
|
if len(data) < 1 {
|
||||||
|
return errors.New("IGMP packet is too small")
|
||||||
|
}
|
||||||
|
|
||||||
|
// common IGMP header values between versions 1..3 of IGMP specification..
|
||||||
|
i.Type = IGMPType(data[0])
|
||||||
|
|
||||||
|
switch i.Type {
|
||||||
|
case IGMPMembershipQuery:
|
||||||
|
i.decodeIGMPv3MembershipQuery(data)
|
||||||
|
case IGMPMembershipReportV3:
|
||||||
|
i.decodeIGMPv3MembershipReport(data)
|
||||||
|
default:
|
||||||
|
return errors.New("unsupported IGMP type")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CanDecode returns the set of layer types that this DecodingLayer can decode.
|
||||||
|
func (i *IGMP) CanDecode() gopacket.LayerClass {
|
||||||
|
return LayerTypeIGMP
|
||||||
|
}
|
||||||
|
|
||||||
|
// NextLayerType returns the layer type contained by this DecodingLayer.
|
||||||
|
func (i *IGMP) NextLayerType() gopacket.LayerType {
|
||||||
|
return gopacket.LayerTypeZero
|
||||||
|
}
|
||||||
|
|
||||||
|
// decodeIGMP will parse IGMP v1,2 or 3 protocols. Checks against the
|
||||||
|
// IGMP type are performed against byte[0], logic then iniitalizes and
|
||||||
|
// passes the appropriate struct (IGMP or IGMPv1or2) to
|
||||||
|
// decodingLayerDecoder.
|
||||||
|
func decodeIGMP(data []byte, p gopacket.PacketBuilder) error {
|
||||||
|
if len(data) < 1 {
|
||||||
|
return errors.New("IGMP packet is too small")
|
||||||
|
}
|
||||||
|
|
||||||
|
// byte 0 contains IGMP message type.
|
||||||
|
switch IGMPType(data[0]) {
|
||||||
|
case IGMPMembershipQuery:
|
||||||
|
// IGMPv3 Membership Query payload is >= 12
|
||||||
|
if len(data) >= 12 {
|
||||||
|
i := &IGMP{Version: 3}
|
||||||
|
return decodingLayerDecoder(i, data, p)
|
||||||
|
} else if len(data) == 8 {
|
||||||
|
i := &IGMPv1or2{}
|
||||||
|
if data[1] == 0x00 {
|
||||||
|
i.Version = 1 // IGMPv1 has a query length of 8 and MaxResp = 0
|
||||||
|
} else {
|
||||||
|
i.Version = 2 // IGMPv2 has a query length of 8 and MaxResp != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
return decodingLayerDecoder(i, data, p)
|
||||||
|
}
|
||||||
|
case IGMPMembershipReportV3:
|
||||||
|
i := &IGMP{Version: 3}
|
||||||
|
return decodingLayerDecoder(i, data, p)
|
||||||
|
case IGMPMembershipReportV1:
|
||||||
|
i := &IGMPv1or2{Version: 1}
|
||||||
|
return decodingLayerDecoder(i, data, p)
|
||||||
|
case IGMPLeaveGroup, IGMPMembershipReportV2:
|
||||||
|
// leave group and Query Report v2 used in IGMPv2 only.
|
||||||
|
i := &IGMPv1or2{Version: 2}
|
||||||
|
return decodingLayerDecoder(i, data, p)
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
|
return errors.New("Unable to determine IGMP type.")
|
||||||
|
}
|
|
@ -0,0 +1,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
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
|
@ -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,
|
||||||
|
})
|
||||||
|
)
|
|
@ -0,0 +1,218 @@
|
||||||
|
// 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"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/google/gopacket"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// LCMShortHeaderMagic is the LCM small message header magic number
|
||||||
|
LCMShortHeaderMagic uint32 = 0x4c433032
|
||||||
|
// LCMFragmentedHeaderMagic is the LCM fragmented message header magic number
|
||||||
|
LCMFragmentedHeaderMagic uint32 = 0x4c433033
|
||||||
|
)
|
||||||
|
|
||||||
|
// LCM (Lightweight Communications and Marshalling) is a set of libraries and
|
||||||
|
// tools for message passing and data marshalling, targeted at real-time systems
|
||||||
|
// where high-bandwidth and low latency are critical. It provides a
|
||||||
|
// publish/subscribe message passing model and automatic
|
||||||
|
// marshalling/unmarshalling code generation with bindings for applications in a
|
||||||
|
// variety of programming languages.
|
||||||
|
//
|
||||||
|
// References
|
||||||
|
// https://lcm-proj.github.io/
|
||||||
|
// https://github.com/lcm-proj/lcm
|
||||||
|
type LCM struct {
|
||||||
|
// Common (short & fragmented header) fields
|
||||||
|
Magic uint32
|
||||||
|
SequenceNumber uint32
|
||||||
|
// Fragmented header only fields
|
||||||
|
PayloadSize uint32
|
||||||
|
FragmentOffset uint32
|
||||||
|
FragmentNumber uint16
|
||||||
|
TotalFragments uint16
|
||||||
|
// Common field
|
||||||
|
ChannelName string
|
||||||
|
// Gopacket helper fields
|
||||||
|
Fragmented bool
|
||||||
|
fingerprint LCMFingerprint
|
||||||
|
contents []byte
|
||||||
|
payload []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
// LCMFingerprint is the type of a LCM fingerprint.
|
||||||
|
type LCMFingerprint uint64
|
||||||
|
|
||||||
|
var (
|
||||||
|
// lcmLayerTypes contains a map of all LCM fingerprints that we support and
|
||||||
|
// their LayerType
|
||||||
|
lcmLayerTypes = map[LCMFingerprint]gopacket.LayerType{}
|
||||||
|
layerTypeIndex = 1001
|
||||||
|
)
|
||||||
|
|
||||||
|
// RegisterLCMLayerType allows users to register decoders for the underlying
|
||||||
|
// LCM payload. This is done based on the fingerprint that every LCM message
|
||||||
|
// contains and which identifies it uniquely. If num is not the zero value it
|
||||||
|
// will be used when registering with RegisterLayerType towards gopacket,
|
||||||
|
// otherwise an incremental value starting from 1001 will be used.
|
||||||
|
func RegisterLCMLayerType(num int, name string, fingerprint LCMFingerprint,
|
||||||
|
decoder gopacket.Decoder) gopacket.LayerType {
|
||||||
|
metadata := gopacket.LayerTypeMetadata{Name: name, Decoder: decoder}
|
||||||
|
|
||||||
|
if num == 0 {
|
||||||
|
num = layerTypeIndex
|
||||||
|
layerTypeIndex++
|
||||||
|
}
|
||||||
|
|
||||||
|
lcmLayerTypes[fingerprint] = gopacket.RegisterLayerType(num, metadata)
|
||||||
|
|
||||||
|
return lcmLayerTypes[fingerprint]
|
||||||
|
}
|
||||||
|
|
||||||
|
// SupportedLCMFingerprints returns a slice of all LCM fingerprints that has
|
||||||
|
// been registered so far.
|
||||||
|
func SupportedLCMFingerprints() []LCMFingerprint {
|
||||||
|
fingerprints := make([]LCMFingerprint, 0, len(lcmLayerTypes))
|
||||||
|
for fp := range lcmLayerTypes {
|
||||||
|
fingerprints = append(fingerprints, fp)
|
||||||
|
}
|
||||||
|
return fingerprints
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetLCMLayerType returns the underlying LCM message's LayerType.
|
||||||
|
// This LayerType has to be registered by using RegisterLCMLayerType.
|
||||||
|
func GetLCMLayerType(fingerprint LCMFingerprint) gopacket.LayerType {
|
||||||
|
layerType, ok := lcmLayerTypes[fingerprint]
|
||||||
|
if !ok {
|
||||||
|
return gopacket.LayerTypePayload
|
||||||
|
}
|
||||||
|
|
||||||
|
return layerType
|
||||||
|
}
|
||||||
|
|
||||||
|
func decodeLCM(data []byte, p gopacket.PacketBuilder) error {
|
||||||
|
lcm := &LCM{}
|
||||||
|
|
||||||
|
err := lcm.DecodeFromBytes(data, p)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
p.AddLayer(lcm)
|
||||||
|
p.SetApplicationLayer(lcm)
|
||||||
|
|
||||||
|
return p.NextDecoder(lcm.NextLayerType())
|
||||||
|
}
|
||||||
|
|
||||||
|
// DecodeFromBytes decodes the given bytes into this layer.
|
||||||
|
func (lcm *LCM) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
|
||||||
|
if len(data) < 8 {
|
||||||
|
df.SetTruncated()
|
||||||
|
return errors.New("LCM < 8 bytes")
|
||||||
|
}
|
||||||
|
offset := 0
|
||||||
|
|
||||||
|
lcm.Magic = binary.BigEndian.Uint32(data[offset:4])
|
||||||
|
offset += 4
|
||||||
|
|
||||||
|
if lcm.Magic != LCMShortHeaderMagic && lcm.Magic != LCMFragmentedHeaderMagic {
|
||||||
|
return fmt.Errorf("Received LCM header magic %v does not match know "+
|
||||||
|
"LCM magic numbers. Dropping packet.", lcm.Magic)
|
||||||
|
}
|
||||||
|
|
||||||
|
lcm.SequenceNumber = binary.BigEndian.Uint32(data[offset:8])
|
||||||
|
offset += 4
|
||||||
|
|
||||||
|
if lcm.Magic == LCMFragmentedHeaderMagic {
|
||||||
|
lcm.Fragmented = true
|
||||||
|
|
||||||
|
lcm.PayloadSize = binary.BigEndian.Uint32(data[offset : offset+4])
|
||||||
|
offset += 4
|
||||||
|
|
||||||
|
lcm.FragmentOffset = binary.BigEndian.Uint32(data[offset : offset+4])
|
||||||
|
offset += 4
|
||||||
|
|
||||||
|
lcm.FragmentNumber = binary.BigEndian.Uint16(data[offset : offset+2])
|
||||||
|
offset += 2
|
||||||
|
|
||||||
|
lcm.TotalFragments = binary.BigEndian.Uint16(data[offset : offset+2])
|
||||||
|
offset += 2
|
||||||
|
} else {
|
||||||
|
lcm.Fragmented = false
|
||||||
|
}
|
||||||
|
|
||||||
|
if !lcm.Fragmented || (lcm.Fragmented && lcm.FragmentNumber == 0) {
|
||||||
|
buffer := make([]byte, 0)
|
||||||
|
for _, b := range data[offset:] {
|
||||||
|
offset++
|
||||||
|
|
||||||
|
if b == 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer = append(buffer, b)
|
||||||
|
}
|
||||||
|
|
||||||
|
lcm.ChannelName = string(buffer)
|
||||||
|
}
|
||||||
|
|
||||||
|
lcm.fingerprint = LCMFingerprint(
|
||||||
|
binary.BigEndian.Uint64(data[offset : offset+8]))
|
||||||
|
|
||||||
|
lcm.contents = data[:offset]
|
||||||
|
lcm.payload = data[offset:]
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CanDecode returns a set of layers that LCM objects can decode.
|
||||||
|
// As LCM objects can only decode the LCM layer, we just return that layer.
|
||||||
|
func (lcm LCM) CanDecode() gopacket.LayerClass {
|
||||||
|
return LayerTypeLCM
|
||||||
|
}
|
||||||
|
|
||||||
|
// NextLayerType specifies the LCM payload layer type following this header.
|
||||||
|
// As LCM packets are serialized structs with uniq fingerprints for each uniq
|
||||||
|
// combination of data types, lookup of correct layer type is based on that
|
||||||
|
// fingerprint.
|
||||||
|
func (lcm LCM) NextLayerType() gopacket.LayerType {
|
||||||
|
if !lcm.Fragmented || (lcm.Fragmented && lcm.FragmentNumber == 0) {
|
||||||
|
return GetLCMLayerType(lcm.fingerprint)
|
||||||
|
}
|
||||||
|
|
||||||
|
return gopacket.LayerTypeFragment
|
||||||
|
}
|
||||||
|
|
||||||
|
// LayerType returns LayerTypeLCM
|
||||||
|
func (lcm LCM) LayerType() gopacket.LayerType {
|
||||||
|
return LayerTypeLCM
|
||||||
|
}
|
||||||
|
|
||||||
|
// LayerContents returns the contents of the LCM header.
|
||||||
|
func (lcm LCM) LayerContents() []byte {
|
||||||
|
return lcm.contents
|
||||||
|
}
|
||||||
|
|
||||||
|
// LayerPayload returns the payload following this LCM header.
|
||||||
|
func (lcm LCM) LayerPayload() []byte {
|
||||||
|
return lcm.payload
|
||||||
|
}
|
||||||
|
|
||||||
|
// Payload returns the payload following this LCM header.
|
||||||
|
func (lcm LCM) Payload() []byte {
|
||||||
|
return lcm.LayerPayload()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fingerprint returns the LCM fingerprint of the underlying message.
|
||||||
|
func (lcm LCM) Fingerprint() LCMFingerprint {
|
||||||
|
return lcm.fingerprint
|
||||||
|
}
|
|
@ -0,0 +1,98 @@
|
||||||
|
// 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"
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
|
||||||
|
"github.com/google/gopacket"
|
||||||
|
)
|
||||||
|
|
||||||
|
type LinuxSLLPacketType uint16
|
||||||
|
|
||||||
|
const (
|
||||||
|
LinuxSLLPacketTypeHost LinuxSLLPacketType = 0 // To us
|
||||||
|
LinuxSLLPacketTypeBroadcast LinuxSLLPacketType = 1 // To all
|
||||||
|
LinuxSLLPacketTypeMulticast LinuxSLLPacketType = 2 // To group
|
||||||
|
LinuxSLLPacketTypeOtherhost LinuxSLLPacketType = 3 // To someone else
|
||||||
|
LinuxSLLPacketTypeOutgoing LinuxSLLPacketType = 4 // Outgoing of any type
|
||||||
|
// These ones are invisible by user level
|
||||||
|
LinuxSLLPacketTypeLoopback LinuxSLLPacketType = 5 // MC/BRD frame looped back
|
||||||
|
LinuxSLLPacketTypeFastroute LinuxSLLPacketType = 6 // Fastrouted frame
|
||||||
|
)
|
||||||
|
|
||||||
|
func (l LinuxSLLPacketType) String() string {
|
||||||
|
switch l {
|
||||||
|
case LinuxSLLPacketTypeHost:
|
||||||
|
return "host"
|
||||||
|
case LinuxSLLPacketTypeBroadcast:
|
||||||
|
return "broadcast"
|
||||||
|
case LinuxSLLPacketTypeMulticast:
|
||||||
|
return "multicast"
|
||||||
|
case LinuxSLLPacketTypeOtherhost:
|
||||||
|
return "otherhost"
|
||||||
|
case LinuxSLLPacketTypeOutgoing:
|
||||||
|
return "outgoing"
|
||||||
|
case LinuxSLLPacketTypeLoopback:
|
||||||
|
return "loopback"
|
||||||
|
case LinuxSLLPacketTypeFastroute:
|
||||||
|
return "fastroute"
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("Unknown(%d)", int(l))
|
||||||
|
}
|
||||||
|
|
||||||
|
type LinuxSLL struct {
|
||||||
|
BaseLayer
|
||||||
|
PacketType LinuxSLLPacketType
|
||||||
|
AddrLen uint16
|
||||||
|
Addr net.HardwareAddr
|
||||||
|
EthernetType EthernetType
|
||||||
|
AddrType uint16
|
||||||
|
}
|
||||||
|
|
||||||
|
// LayerType returns LayerTypeLinuxSLL.
|
||||||
|
func (sll *LinuxSLL) LayerType() gopacket.LayerType { return LayerTypeLinuxSLL }
|
||||||
|
|
||||||
|
func (sll *LinuxSLL) CanDecode() gopacket.LayerClass {
|
||||||
|
return LayerTypeLinuxSLL
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sll *LinuxSLL) LinkFlow() gopacket.Flow {
|
||||||
|
return gopacket.NewFlow(EndpointMAC, sll.Addr, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sll *LinuxSLL) NextLayerType() gopacket.LayerType {
|
||||||
|
return sll.EthernetType.LayerType()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sll *LinuxSLL) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
|
||||||
|
if len(data) < 16 {
|
||||||
|
return errors.New("Linux SLL packet too small")
|
||||||
|
}
|
||||||
|
sll.PacketType = LinuxSLLPacketType(binary.BigEndian.Uint16(data[0:2]))
|
||||||
|
sll.AddrType = binary.BigEndian.Uint16(data[2:4])
|
||||||
|
sll.AddrLen = binary.BigEndian.Uint16(data[4:6])
|
||||||
|
|
||||||
|
sll.Addr = net.HardwareAddr(data[6 : sll.AddrLen+6])
|
||||||
|
sll.EthernetType = EthernetType(binary.BigEndian.Uint16(data[14:16]))
|
||||||
|
sll.BaseLayer = BaseLayer{data[:16], data[16:]}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func decodeLinuxSLL(data []byte, p gopacket.PacketBuilder) error {
|
||||||
|
sll := &LinuxSLL{}
|
||||||
|
if err := sll.DecodeFromBytes(data, p); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
p.AddLayer(sll)
|
||||||
|
p.SetLinkLayer(sll)
|
||||||
|
return p.NextDecoder(sll.EthernetType)
|
||||||
|
}
|
|
@ -0,0 +1,193 @@
|
||||||
|
// 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"
|
||||||
|
)
|
||||||
|
|
||||||
|
// LLC is the layer used for 802.2 Logical Link Control headers.
|
||||||
|
// See http://standards.ieee.org/getieee802/download/802.2-1998.pdf
|
||||||
|
type LLC struct {
|
||||||
|
BaseLayer
|
||||||
|
DSAP uint8
|
||||||
|
IG bool // true means group, false means individual
|
||||||
|
SSAP uint8
|
||||||
|
CR bool // true means response, false means command
|
||||||
|
Control uint16
|
||||||
|
}
|
||||||
|
|
||||||
|
// LayerType returns gopacket.LayerTypeLLC.
|
||||||
|
func (l *LLC) LayerType() gopacket.LayerType { return LayerTypeLLC }
|
||||||
|
|
||||||
|
// DecodeFromBytes decodes the given bytes into this layer.
|
||||||
|
func (l *LLC) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
|
||||||
|
if len(data) < 3 {
|
||||||
|
return errors.New("LLC header too small")
|
||||||
|
}
|
||||||
|
l.DSAP = data[0] & 0xFE
|
||||||
|
l.IG = data[0]&0x1 != 0
|
||||||
|
l.SSAP = data[1] & 0xFE
|
||||||
|
l.CR = data[1]&0x1 != 0
|
||||||
|
l.Control = uint16(data[2])
|
||||||
|
|
||||||
|
if l.Control&0x1 == 0 || l.Control&0x3 == 0x1 {
|
||||||
|
if len(data) < 4 {
|
||||||
|
return errors.New("LLC header too small")
|
||||||
|
}
|
||||||
|
l.Control = l.Control<<8 | uint16(data[3])
|
||||||
|
l.Contents = data[:4]
|
||||||
|
l.Payload = data[4:]
|
||||||
|
} else {
|
||||||
|
l.Contents = data[:3]
|
||||||
|
l.Payload = data[3:]
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CanDecode returns the set of layer types that this DecodingLayer can decode.
|
||||||
|
func (l *LLC) CanDecode() gopacket.LayerClass {
|
||||||
|
return LayerTypeLLC
|
||||||
|
}
|
||||||
|
|
||||||
|
// NextLayerType returns the layer type contained by this DecodingLayer.
|
||||||
|
func (l *LLC) NextLayerType() gopacket.LayerType {
|
||||||
|
switch {
|
||||||
|
case l.DSAP == 0xAA && l.SSAP == 0xAA:
|
||||||
|
return LayerTypeSNAP
|
||||||
|
case l.DSAP == 0x42 && l.SSAP == 0x42:
|
||||||
|
return LayerTypeSTP
|
||||||
|
}
|
||||||
|
return gopacket.LayerTypeZero // Not implemented
|
||||||
|
}
|
||||||
|
|
||||||
|
// SNAP is used inside LLC. See
|
||||||
|
// http://standards.ieee.org/getieee802/download/802-2001.pdf.
|
||||||
|
// From http://en.wikipedia.org/wiki/Subnetwork_Access_Protocol:
|
||||||
|
// "[T]he Subnetwork Access Protocol (SNAP) is a mechanism for multiplexing,
|
||||||
|
// on networks using IEEE 802.2 LLC, more protocols than can be distinguished
|
||||||
|
// by the 8-bit 802.2 Service Access Point (SAP) fields."
|
||||||
|
type SNAP struct {
|
||||||
|
BaseLayer
|
||||||
|
OrganizationalCode []byte
|
||||||
|
Type EthernetType
|
||||||
|
}
|
||||||
|
|
||||||
|
// LayerType returns gopacket.LayerTypeSNAP.
|
||||||
|
func (s *SNAP) LayerType() gopacket.LayerType { return LayerTypeSNAP }
|
||||||
|
|
||||||
|
// DecodeFromBytes decodes the given bytes into this layer.
|
||||||
|
func (s *SNAP) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
|
||||||
|
if len(data) < 5 {
|
||||||
|
return errors.New("SNAP header too small")
|
||||||
|
}
|
||||||
|
s.OrganizationalCode = data[:3]
|
||||||
|
s.Type = EthernetType(binary.BigEndian.Uint16(data[3:5]))
|
||||||
|
s.BaseLayer = BaseLayer{data[:5], data[5:]}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CanDecode returns the set of layer types that this DecodingLayer can decode.
|
||||||
|
func (s *SNAP) CanDecode() gopacket.LayerClass {
|
||||||
|
return LayerTypeSNAP
|
||||||
|
}
|
||||||
|
|
||||||
|
// NextLayerType returns the layer type contained by this DecodingLayer.
|
||||||
|
func (s *SNAP) NextLayerType() gopacket.LayerType {
|
||||||
|
// See BUG(gconnel) in decodeSNAP
|
||||||
|
return s.Type.LayerType()
|
||||||
|
}
|
||||||
|
|
||||||
|
func decodeLLC(data []byte, p gopacket.PacketBuilder) error {
|
||||||
|
l := &LLC{}
|
||||||
|
err := l.DecodeFromBytes(data, p)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
p.AddLayer(l)
|
||||||
|
return p.NextDecoder(l.NextLayerType())
|
||||||
|
}
|
||||||
|
|
||||||
|
func decodeSNAP(data []byte, p gopacket.PacketBuilder) error {
|
||||||
|
s := &SNAP{}
|
||||||
|
err := s.DecodeFromBytes(data, p)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
p.AddLayer(s)
|
||||||
|
// BUG(gconnell): When decoding SNAP, we treat the SNAP type as an Ethernet
|
||||||
|
// type. This may not actually be an ethernet type in all cases,
|
||||||
|
// depending on the organizational code. Right now, we don't check.
|
||||||
|
return p.NextDecoder(s.Type)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SerializeTo writes the serialized form of this layer into the
|
||||||
|
// SerializationBuffer, implementing gopacket.SerializableLayer.
|
||||||
|
// See the docs for gopacket.SerializableLayer for more info.
|
||||||
|
func (l *LLC) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
|
||||||
|
var igFlag, crFlag byte
|
||||||
|
var length int
|
||||||
|
|
||||||
|
if l.Control&0xFF00 != 0 {
|
||||||
|
length = 4
|
||||||
|
} else {
|
||||||
|
length = 3
|
||||||
|
}
|
||||||
|
|
||||||
|
if l.DSAP&0x1 != 0 {
|
||||||
|
return errors.New("DSAP value invalid, should not include IG flag bit")
|
||||||
|
}
|
||||||
|
|
||||||
|
if l.SSAP&0x1 != 0 {
|
||||||
|
return errors.New("SSAP value invalid, should not include CR flag bit")
|
||||||
|
}
|
||||||
|
|
||||||
|
if buf, err := b.PrependBytes(length); err != nil {
|
||||||
|
return err
|
||||||
|
} else {
|
||||||
|
igFlag = 0
|
||||||
|
if l.IG {
|
||||||
|
igFlag = 0x1
|
||||||
|
}
|
||||||
|
|
||||||
|
crFlag = 0
|
||||||
|
if l.CR {
|
||||||
|
crFlag = 0x1
|
||||||
|
}
|
||||||
|
|
||||||
|
buf[0] = l.DSAP + igFlag
|
||||||
|
buf[1] = l.SSAP + crFlag
|
||||||
|
|
||||||
|
if length == 4 {
|
||||||
|
buf[2] = uint8(l.Control >> 8)
|
||||||
|
buf[3] = uint8(l.Control)
|
||||||
|
} else {
|
||||||
|
buf[2] = uint8(l.Control)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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 (s *SNAP) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
|
||||||
|
if buf, err := b.PrependBytes(5); err != nil {
|
||||||
|
return err
|
||||||
|
} else {
|
||||||
|
buf[0] = s.OrganizationalCode[0]
|
||||||
|
buf[1] = s.OrganizationalCode[1]
|
||||||
|
buf[2] = s.OrganizationalCode[2]
|
||||||
|
binary.BigEndian.PutUint16(buf[3:5], uint16(s.Type))
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,80 @@
|
||||||
|
// 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"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/google/gopacket"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Loopback contains the header for loopback encapsulation. This header is
|
||||||
|
// used by both BSD and OpenBSD style loopback decoding (pcap's DLT_NULL
|
||||||
|
// and DLT_LOOP, respectively).
|
||||||
|
type Loopback struct {
|
||||||
|
BaseLayer
|
||||||
|
Family ProtocolFamily
|
||||||
|
}
|
||||||
|
|
||||||
|
// LayerType returns LayerTypeLoopback.
|
||||||
|
func (l *Loopback) LayerType() gopacket.LayerType { return LayerTypeLoopback }
|
||||||
|
|
||||||
|
// DecodeFromBytes decodes the given bytes into this layer.
|
||||||
|
func (l *Loopback) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
|
||||||
|
if len(data) < 4 {
|
||||||
|
return errors.New("Loopback packet too small")
|
||||||
|
}
|
||||||
|
|
||||||
|
// The protocol could be either big-endian or little-endian, we're
|
||||||
|
// not sure. But we're PRETTY sure that the value is less than
|
||||||
|
// 256, so we can check the first two bytes.
|
||||||
|
var prot uint32
|
||||||
|
if data[0] == 0 && data[1] == 0 {
|
||||||
|
prot = binary.BigEndian.Uint32(data[:4])
|
||||||
|
} else {
|
||||||
|
prot = binary.LittleEndian.Uint32(data[:4])
|
||||||
|
}
|
||||||
|
if prot > 0xFF {
|
||||||
|
return fmt.Errorf("Invalid loopback protocol %q", data[:4])
|
||||||
|
}
|
||||||
|
|
||||||
|
l.Family = ProtocolFamily(prot)
|
||||||
|
l.BaseLayer = BaseLayer{data[:4], data[4:]}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CanDecode returns the set of layer types that this DecodingLayer can decode.
|
||||||
|
func (l *Loopback) CanDecode() gopacket.LayerClass {
|
||||||
|
return LayerTypeLoopback
|
||||||
|
}
|
||||||
|
|
||||||
|
// NextLayerType returns the layer type contained by this DecodingLayer.
|
||||||
|
func (l *Loopback) NextLayerType() gopacket.LayerType {
|
||||||
|
return l.Family.LayerType()
|
||||||
|
}
|
||||||
|
|
||||||
|
// SerializeTo writes the serialized form of this layer into the
|
||||||
|
// SerializationBuffer, implementing gopacket.SerializableLayer.
|
||||||
|
func (l *Loopback) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
|
||||||
|
bytes, err := b.PrependBytes(4)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
binary.LittleEndian.PutUint32(bytes, uint32(l.Family))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func decodeLoopback(data []byte, p gopacket.PacketBuilder) error {
|
||||||
|
l := Loopback{}
|
||||||
|
if err := l.DecodeFromBytes(data, gopacket.NilDecodeFeedback); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
p.AddLayer(&l)
|
||||||
|
return p.NextDecoder(l.Family)
|
||||||
|
}
|
|
@ -0,0 +1,182 @@
|
||||||
|
// Copyright 2018 GoPacket Authors. All rights reserved.
|
||||||
|
//
|
||||||
|
// Use of this source code is governed by a BSD-style license
|
||||||
|
// that can be found in the LICENSE file in the root of the source
|
||||||
|
// tree.
|
||||||
|
|
||||||
|
package layers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/binary"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"math"
|
||||||
|
"net"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/google/gopacket"
|
||||||
|
)
|
||||||
|
|
||||||
|
// MLDv1Message represents the common structure of all MLDv1 messages
|
||||||
|
type MLDv1Message struct {
|
||||||
|
BaseLayer
|
||||||
|
// 3.4. Maximum Response Delay
|
||||||
|
MaximumResponseDelay time.Duration
|
||||||
|
// 3.6. Multicast Address
|
||||||
|
// Zero in general query
|
||||||
|
// Specific IPv6 multicast address otherwise
|
||||||
|
MulticastAddress net.IP
|
||||||
|
}
|
||||||
|
|
||||||
|
// DecodeFromBytes decodes the given bytes into this layer.
|
||||||
|
func (m *MLDv1Message) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
|
||||||
|
if len(data) < 20 {
|
||||||
|
df.SetTruncated()
|
||||||
|
return errors.New("ICMP layer less than 20 bytes for Multicast Listener Query Message V1")
|
||||||
|
}
|
||||||
|
|
||||||
|
m.MaximumResponseDelay = time.Duration(binary.BigEndian.Uint16(data[0:2])) * time.Millisecond
|
||||||
|
// data[2:4] is reserved and not used in mldv1
|
||||||
|
m.MulticastAddress = data[4:20]
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NextLayerType returns the layer type contained by this DecodingLayer.
|
||||||
|
func (*MLDv1Message) NextLayerType() gopacket.LayerType {
|
||||||
|
return gopacket.LayerTypeZero
|
||||||
|
}
|
||||||
|
|
||||||
|
// SerializeTo writes the serialized form of this layer into the
|
||||||
|
// SerializationBuffer, implementing gopacket.SerializableLayer.
|
||||||
|
// See the docs for gopacket.SerializableLayer for more info.
|
||||||
|
func (m *MLDv1Message) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
|
||||||
|
buf, err := b.PrependBytes(20)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if m.MaximumResponseDelay < 0 {
|
||||||
|
return errors.New("maximum response delay must not be negative")
|
||||||
|
}
|
||||||
|
dms := m.MaximumResponseDelay / time.Millisecond
|
||||||
|
if dms > math.MaxUint16 {
|
||||||
|
return fmt.Errorf("maximum response delay %dms is more than the allowed 65535ms", dms)
|
||||||
|
}
|
||||||
|
binary.BigEndian.PutUint16(buf[0:2], uint16(dms))
|
||||||
|
|
||||||
|
copy(buf[2:4], []byte{0x0, 0x0})
|
||||||
|
|
||||||
|
ma16 := m.MulticastAddress.To16()
|
||||||
|
if ma16 == nil {
|
||||||
|
return fmt.Errorf("invalid multicast address '%s'", m.MulticastAddress)
|
||||||
|
}
|
||||||
|
copy(buf[4:20], ma16)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sums this layer up nicely formatted
|
||||||
|
func (m *MLDv1Message) String() string {
|
||||||
|
return fmt.Sprintf(
|
||||||
|
"Maximum Response Delay: %dms, Multicast Address: %s",
|
||||||
|
m.MaximumResponseDelay/time.Millisecond,
|
||||||
|
m.MulticastAddress)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MLDv1MulticastListenerQueryMessage are sent by the router to determine
|
||||||
|
// whether there are multicast listeners on the link.
|
||||||
|
// https://tools.ietf.org/html/rfc2710 Page 5
|
||||||
|
type MLDv1MulticastListenerQueryMessage struct {
|
||||||
|
MLDv1Message
|
||||||
|
}
|
||||||
|
|
||||||
|
// DecodeFromBytes decodes the given bytes into this layer.
|
||||||
|
func (m *MLDv1MulticastListenerQueryMessage) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
|
||||||
|
err := m.MLDv1Message.DecodeFromBytes(data, df)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(data) > 20 {
|
||||||
|
m.Payload = data[20:]
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// LayerType returns LayerTypeMLDv1MulticastListenerQuery.
|
||||||
|
func (*MLDv1MulticastListenerQueryMessage) LayerType() gopacket.LayerType {
|
||||||
|
return LayerTypeMLDv1MulticastListenerQuery
|
||||||
|
}
|
||||||
|
|
||||||
|
// CanDecode returns the set of layer types that this DecodingLayer can decode.
|
||||||
|
func (*MLDv1MulticastListenerQueryMessage) CanDecode() gopacket.LayerClass {
|
||||||
|
return LayerTypeMLDv1MulticastListenerQuery
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsGeneralQuery is true when this is a general query.
|
||||||
|
// In a Query message, the Multicast Address field is set to zero when
|
||||||
|
// sending a General Query.
|
||||||
|
// https://tools.ietf.org/html/rfc2710#section-3.6
|
||||||
|
func (m *MLDv1MulticastListenerQueryMessage) IsGeneralQuery() bool {
|
||||||
|
return net.IPv6zero.Equal(m.MulticastAddress)
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsSpecificQuery is true when this is not a general query.
|
||||||
|
// In a Query message, the Multicast Address field is set to a specific
|
||||||
|
// IPv6 multicast address when sending a Multicast-Address-Specific Query.
|
||||||
|
// https://tools.ietf.org/html/rfc2710#section-3.6
|
||||||
|
func (m *MLDv1MulticastListenerQueryMessage) IsSpecificQuery() bool {
|
||||||
|
return !m.IsGeneralQuery()
|
||||||
|
}
|
||||||
|
|
||||||
|
// MLDv1MulticastListenerReportMessage is sent by a client listening on
|
||||||
|
// a specific multicast address to indicate that it is (still) listening
|
||||||
|
// on the specific multicast address.
|
||||||
|
// https://tools.ietf.org/html/rfc2710 Page 6
|
||||||
|
type MLDv1MulticastListenerReportMessage struct {
|
||||||
|
MLDv1Message
|
||||||
|
}
|
||||||
|
|
||||||
|
// LayerType returns LayerTypeMLDv1MulticastListenerReport.
|
||||||
|
func (*MLDv1MulticastListenerReportMessage) LayerType() gopacket.LayerType {
|
||||||
|
return LayerTypeMLDv1MulticastListenerReport
|
||||||
|
}
|
||||||
|
|
||||||
|
// CanDecode returns the set of layer types that this DecodingLayer can decode.
|
||||||
|
func (*MLDv1MulticastListenerReportMessage) CanDecode() gopacket.LayerClass {
|
||||||
|
return LayerTypeMLDv1MulticastListenerReport
|
||||||
|
}
|
||||||
|
|
||||||
|
// MLDv1MulticastListenerDoneMessage should be sent by a client when it ceases
|
||||||
|
// to listen to a multicast address on an interface.
|
||||||
|
// https://tools.ietf.org/html/rfc2710 Page 7
|
||||||
|
type MLDv1MulticastListenerDoneMessage struct {
|
||||||
|
MLDv1Message
|
||||||
|
}
|
||||||
|
|
||||||
|
// LayerType returns LayerTypeMLDv1MulticastListenerDone.
|
||||||
|
func (*MLDv1MulticastListenerDoneMessage) LayerType() gopacket.LayerType {
|
||||||
|
return LayerTypeMLDv1MulticastListenerDone
|
||||||
|
}
|
||||||
|
|
||||||
|
// CanDecode returns the set of layer types that this DecodingLayer can decode.
|
||||||
|
func (*MLDv1MulticastListenerDoneMessage) CanDecode() gopacket.LayerClass {
|
||||||
|
return LayerTypeMLDv1MulticastListenerDone
|
||||||
|
}
|
||||||
|
|
||||||
|
func decodeMLDv1MulticastListenerReport(data []byte, p gopacket.PacketBuilder) error {
|
||||||
|
m := &MLDv1MulticastListenerReportMessage{}
|
||||||
|
return decodingLayerDecoder(m, data, p)
|
||||||
|
}
|
||||||
|
|
||||||
|
func decodeMLDv1MulticastListenerQuery(data []byte, p gopacket.PacketBuilder) error {
|
||||||
|
m := &MLDv1MulticastListenerQueryMessage{}
|
||||||
|
return decodingLayerDecoder(m, data, p)
|
||||||
|
}
|
||||||
|
|
||||||
|
func decodeMLDv1MulticastListenerDone(data []byte, p gopacket.PacketBuilder) error {
|
||||||
|
m := &MLDv1MulticastListenerDoneMessage{}
|
||||||
|
return decodingLayerDecoder(m, data, p)
|
||||||
|
}
|
|
@ -0,0 +1,619 @@
|
||||||
|
// Copyright 2018 GoPacket Authors. All rights reserved.
|
||||||
|
//
|
||||||
|
// Use of this source code is governed by a BSD-style license
|
||||||
|
// that can be found in the LICENSE file in the root of the source
|
||||||
|
// tree.
|
||||||
|
|
||||||
|
package layers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/binary"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"math"
|
||||||
|
"net"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/google/gopacket"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// S Flag bit is 1
|
||||||
|
mldv2STrue uint8 = 0x8
|
||||||
|
|
||||||
|
// S Flag value mask
|
||||||
|
// mldv2STrue & mldv2SMask == mldv2STrue // true
|
||||||
|
// 0x1 & mldv2SMask == mldv2STrue // true
|
||||||
|
// 0x0 & mldv2SMask == mldv2STrue // false
|
||||||
|
mldv2SMask uint8 = 0x8
|
||||||
|
|
||||||
|
// QRV value mask
|
||||||
|
mldv2QRVMask uint8 = 0x7
|
||||||
|
)
|
||||||
|
|
||||||
|
// MLDv2MulticastListenerQueryMessage are sent by multicast routers to query the
|
||||||
|
// multicast listening state of neighboring interfaces.
|
||||||
|
// https://tools.ietf.org/html/rfc3810#section-5.1
|
||||||
|
//
|
||||||
|
// Some information, like Maximum Response Code and Multicast Address are in the
|
||||||
|
// previous layer LayerTypeMLDv1MulticastListenerQuery
|
||||||
|
type MLDv2MulticastListenerQueryMessage struct {
|
||||||
|
BaseLayer
|
||||||
|
// 5.1.3. Maximum Response Delay COde
|
||||||
|
MaximumResponseCode uint16
|
||||||
|
// 5.1.5. Multicast Address
|
||||||
|
// Zero in general query
|
||||||
|
// Specific IPv6 multicast address otherwise
|
||||||
|
MulticastAddress net.IP
|
||||||
|
// 5.1.7. S Flag (Suppress Router-Side Processing)
|
||||||
|
SuppressRoutersideProcessing bool
|
||||||
|
// 5.1.8. QRV (Querier's Robustness Variable)
|
||||||
|
QueriersRobustnessVariable uint8
|
||||||
|
// 5.1.9. QQIC (Querier's Query Interval Code)
|
||||||
|
QueriersQueryIntervalCode uint8
|
||||||
|
// 5.1.10. Number of Sources (N)
|
||||||
|
NumberOfSources uint16
|
||||||
|
// 5.1.11 Source Address [i]
|
||||||
|
SourceAddresses []net.IP
|
||||||
|
}
|
||||||
|
|
||||||
|
// DecodeFromBytes decodes the given bytes into this layer.
|
||||||
|
func (m *MLDv2MulticastListenerQueryMessage) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
|
||||||
|
if len(data) < 24 {
|
||||||
|
df.SetTruncated()
|
||||||
|
return errors.New("ICMP layer less than 24 bytes for Multicast Listener Query Message V2")
|
||||||
|
}
|
||||||
|
|
||||||
|
m.MaximumResponseCode = binary.BigEndian.Uint16(data[0:2])
|
||||||
|
// ignore data[2:4] as per https://tools.ietf.org/html/rfc3810#section-5.1.4
|
||||||
|
m.MulticastAddress = data[4:20]
|
||||||
|
m.SuppressRoutersideProcessing = (data[20] & mldv2SMask) == mldv2STrue
|
||||||
|
m.QueriersRobustnessVariable = data[20] & mldv2QRVMask
|
||||||
|
m.QueriersQueryIntervalCode = data[21]
|
||||||
|
|
||||||
|
m.NumberOfSources = binary.BigEndian.Uint16(data[22:24])
|
||||||
|
|
||||||
|
var end int
|
||||||
|
for i := uint16(0); i < m.NumberOfSources; i++ {
|
||||||
|
begin := 24 + (int(i) * 16)
|
||||||
|
end = begin + 16
|
||||||
|
|
||||||
|
if end > len(data) {
|
||||||
|
df.SetTruncated()
|
||||||
|
return fmt.Errorf("ICMP layer less than %d bytes for Multicast Listener Query Message V2", end)
|
||||||
|
}
|
||||||
|
|
||||||
|
m.SourceAddresses = append(m.SourceAddresses, data[begin:end])
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NextLayerType returns the layer type contained by this DecodingLayer.
|
||||||
|
func (*MLDv2MulticastListenerQueryMessage) NextLayerType() gopacket.LayerType {
|
||||||
|
return gopacket.LayerTypeZero
|
||||||
|
}
|
||||||
|
|
||||||
|
// SerializeTo writes the serialized form of this layer into the
|
||||||
|
// SerializationBuffer, implementing gopacket.SerializableLayer.
|
||||||
|
// See the docs for gopacket.SerializableLayer for more info.
|
||||||
|
func (m *MLDv2MulticastListenerQueryMessage) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
|
||||||
|
if err := m.serializeSourceAddressesTo(b, opts); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
buf, err := b.PrependBytes(24)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
binary.BigEndian.PutUint16(buf[0:2], m.MaximumResponseCode)
|
||||||
|
copy(buf[2:4], []byte{0x00, 0x00}) // set reserved bytes to zero
|
||||||
|
|
||||||
|
ma16 := m.MulticastAddress.To16()
|
||||||
|
if ma16 == nil {
|
||||||
|
return fmt.Errorf("invalid MulticastAddress '%s'", m.MulticastAddress)
|
||||||
|
}
|
||||||
|
copy(buf[4:20], ma16)
|
||||||
|
|
||||||
|
byte20 := m.QueriersRobustnessVariable & mldv2QRVMask
|
||||||
|
if m.SuppressRoutersideProcessing {
|
||||||
|
byte20 |= mldv2STrue
|
||||||
|
} else {
|
||||||
|
byte20 &= ^mldv2STrue // the complement of mldv2STrue
|
||||||
|
}
|
||||||
|
byte20 &= 0x0F // set reserved bits to zero
|
||||||
|
buf[20] = byte20
|
||||||
|
|
||||||
|
binary.BigEndian.PutUint16(buf[22:24], m.NumberOfSources)
|
||||||
|
buf[21] = m.QueriersQueryIntervalCode
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// writes each source address to the buffer preserving the order
|
||||||
|
func (m *MLDv2MulticastListenerQueryMessage) serializeSourceAddressesTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
|
||||||
|
numberOfSourceAddresses := len(m.SourceAddresses)
|
||||||
|
if numberOfSourceAddresses > math.MaxUint16 {
|
||||||
|
return fmt.Errorf(
|
||||||
|
"there are more than %d source addresses, but 65535 is the maximum number of supported addresses",
|
||||||
|
numberOfSourceAddresses)
|
||||||
|
}
|
||||||
|
|
||||||
|
if opts.FixLengths {
|
||||||
|
m.NumberOfSources = uint16(numberOfSourceAddresses)
|
||||||
|
}
|
||||||
|
|
||||||
|
lastSAIdx := numberOfSourceAddresses - 1
|
||||||
|
for k := range m.SourceAddresses {
|
||||||
|
i := lastSAIdx - k // reverse order
|
||||||
|
|
||||||
|
buf, err := b.PrependBytes(16)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
sa16 := m.SourceAddresses[i].To16()
|
||||||
|
if sa16 == nil {
|
||||||
|
return fmt.Errorf("invalid source address [%d] '%s'", i, m.SourceAddresses[i])
|
||||||
|
}
|
||||||
|
copy(buf[0:16], sa16)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// String sums this layer up nicely formatted
|
||||||
|
func (m *MLDv2MulticastListenerQueryMessage) String() string {
|
||||||
|
return fmt.Sprintf(
|
||||||
|
"Maximum Response Code: %#x (%dms), Multicast Address: %s, Suppress Routerside Processing: %t, QRV: %#x, QQIC: %#x (%ds), Number of Source Address: %d (actual: %d), Source Addresses: %s",
|
||||||
|
m.MaximumResponseCode,
|
||||||
|
m.MaximumResponseDelay(),
|
||||||
|
m.MulticastAddress,
|
||||||
|
m.SuppressRoutersideProcessing,
|
||||||
|
m.QueriersRobustnessVariable,
|
||||||
|
m.QueriersQueryIntervalCode,
|
||||||
|
m.QQI()/time.Second,
|
||||||
|
m.NumberOfSources,
|
||||||
|
len(m.SourceAddresses),
|
||||||
|
m.SourceAddresses)
|
||||||
|
}
|
||||||
|
|
||||||
|
// LayerType returns LayerTypeMLDv2MulticastListenerQuery.
|
||||||
|
func (*MLDv2MulticastListenerQueryMessage) LayerType() gopacket.LayerType {
|
||||||
|
return LayerTypeMLDv2MulticastListenerQuery
|
||||||
|
}
|
||||||
|
|
||||||
|
// CanDecode returns the set of layer types that this DecodingLayer can decode.
|
||||||
|
func (*MLDv2MulticastListenerQueryMessage) CanDecode() gopacket.LayerClass {
|
||||||
|
return LayerTypeMLDv2MulticastListenerQuery
|
||||||
|
}
|
||||||
|
|
||||||
|
// QQI calculates the Querier's Query Interval based on the QQIC
|
||||||
|
// according to https://tools.ietf.org/html/rfc3810#section-5.1.9
|
||||||
|
func (m *MLDv2MulticastListenerQueryMessage) QQI() time.Duration {
|
||||||
|
data := m.QueriersQueryIntervalCode
|
||||||
|
if data < 128 {
|
||||||
|
return time.Second * time.Duration(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
exp := uint16(data) & 0x70 >> 4
|
||||||
|
mant := uint16(data) & 0x0F
|
||||||
|
return time.Second * time.Duration(mant|0x1000<<(exp+3))
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetQQI calculates and updates the Querier's Query Interval Code (QQIC)
|
||||||
|
// according to https://tools.ietf.org/html/rfc3810#section-5.1.9
|
||||||
|
func (m *MLDv2MulticastListenerQueryMessage) SetQQI(d time.Duration) error {
|
||||||
|
if d < 0 {
|
||||||
|
m.QueriersQueryIntervalCode = 0
|
||||||
|
return errors.New("QQI duration is negative")
|
||||||
|
}
|
||||||
|
|
||||||
|
if d == 0 {
|
||||||
|
m.QueriersQueryIntervalCode = 0
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
dms := d / time.Second
|
||||||
|
if dms < 128 {
|
||||||
|
m.QueriersQueryIntervalCode = uint8(dms)
|
||||||
|
}
|
||||||
|
|
||||||
|
if dms > 31744 { // mant=0xF, exp=0x7
|
||||||
|
m.QueriersQueryIntervalCode = 0xFF
|
||||||
|
return fmt.Errorf("QQI duration %ds is, maximum allowed is 31744s", dms)
|
||||||
|
}
|
||||||
|
|
||||||
|
value := uint16(dms) // ok, because 31744 < math.MaxUint16
|
||||||
|
exp := uint8(7)
|
||||||
|
for mask := uint16(0x4000); exp > 0; exp-- {
|
||||||
|
if mask&value != 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
mask >>= 1
|
||||||
|
}
|
||||||
|
|
||||||
|
mant := uint8(0x000F & (value >> (exp + 3)))
|
||||||
|
sig := uint8(0x10)
|
||||||
|
m.QueriersQueryIntervalCode = sig | exp<<4 | mant
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// MaximumResponseDelay returns the Maximum Response Delay based on the
|
||||||
|
// Maximum Response Code according to
|
||||||
|
// https://tools.ietf.org/html/rfc3810#section-5.1.3
|
||||||
|
func (m *MLDv2MulticastListenerQueryMessage) MaximumResponseDelay() time.Duration {
|
||||||
|
if m.MaximumResponseCode < 0x8000 {
|
||||||
|
return time.Duration(m.MaximumResponseCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
exp := m.MaximumResponseCode & 0x7000 >> 12
|
||||||
|
mant := m.MaximumResponseCode & 0x0FFF
|
||||||
|
|
||||||
|
return time.Millisecond * time.Duration(mant|0x1000<<(exp+3))
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetMLDv2MaximumResponseDelay updates the Maximum Response Code according to
|
||||||
|
// https://tools.ietf.org/html/rfc3810#section-5.1.3
|
||||||
|
func (m *MLDv2MulticastListenerQueryMessage) SetMLDv2MaximumResponseDelay(d time.Duration) error {
|
||||||
|
if d == 0 {
|
||||||
|
m.MaximumResponseCode = 0
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if d < 0 {
|
||||||
|
return errors.New("maximum response delay must not be negative")
|
||||||
|
}
|
||||||
|
|
||||||
|
dms := d / time.Millisecond
|
||||||
|
|
||||||
|
if dms < 32768 {
|
||||||
|
m.MaximumResponseCode = uint16(dms)
|
||||||
|
}
|
||||||
|
|
||||||
|
if dms > 4193280 { // mant=0xFFF, exp=0x7
|
||||||
|
return fmt.Errorf("maximum response delay %dms is bigger the than maximum of 4193280ms", dms)
|
||||||
|
}
|
||||||
|
|
||||||
|
value := uint32(dms) // ok, because 4193280 < math.MaxUint32
|
||||||
|
exp := uint8(7)
|
||||||
|
for mask := uint32(0x40000000); exp > 0; exp-- {
|
||||||
|
if mask&value != 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
mask >>= 1
|
||||||
|
}
|
||||||
|
|
||||||
|
mant := uint16(0x00000FFF & (value >> (exp + 3)))
|
||||||
|
sig := uint16(0x1000)
|
||||||
|
m.MaximumResponseCode = sig | uint16(exp)<<12 | mant
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// MLDv2MulticastListenerReportMessage is sent by an IP node to report the
|
||||||
|
// current multicast listening state, or changes therein.
|
||||||
|
// https://tools.ietf.org/html/rfc3810#section-5.2
|
||||||
|
type MLDv2MulticastListenerReportMessage struct {
|
||||||
|
BaseLayer
|
||||||
|
// 5.2.3. Nr of Mcast Address Records
|
||||||
|
NumberOfMulticastAddressRecords uint16
|
||||||
|
// 5.2.4. Multicast Address Record [i]
|
||||||
|
MulticastAddressRecords []MLDv2MulticastAddressRecord
|
||||||
|
}
|
||||||
|
|
||||||
|
// DecodeFromBytes decodes the given bytes into this layer.
|
||||||
|
func (m *MLDv2MulticastListenerReportMessage) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
|
||||||
|
if len(data) < 4 {
|
||||||
|
df.SetTruncated()
|
||||||
|
return errors.New("ICMP layer less than 4 bytes for Multicast Listener Report Message V2")
|
||||||
|
}
|
||||||
|
|
||||||
|
// ignore data[0:2] as per RFC
|
||||||
|
// https://tools.ietf.org/html/rfc3810#section-5.2.1
|
||||||
|
m.NumberOfMulticastAddressRecords = binary.BigEndian.Uint16(data[2:4])
|
||||||
|
|
||||||
|
begin := 4
|
||||||
|
for i := uint16(0); i < m.NumberOfMulticastAddressRecords; i++ {
|
||||||
|
mar := MLDv2MulticastAddressRecord{}
|
||||||
|
read, err := mar.decode(data[begin:], df)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
m.MulticastAddressRecords = append(m.MulticastAddressRecords, mar)
|
||||||
|
|
||||||
|
begin += read
|
||||||
|
}
|
||||||
|
|
||||||
|
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 (m *MLDv2MulticastListenerReportMessage) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
|
||||||
|
lastItemIdx := len(m.MulticastAddressRecords) - 1
|
||||||
|
for k := range m.MulticastAddressRecords {
|
||||||
|
i := lastItemIdx - k // reverse order
|
||||||
|
|
||||||
|
err := m.MulticastAddressRecords[i].serializeTo(b, opts)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if opts.FixLengths {
|
||||||
|
numberOfMAR := len(m.MulticastAddressRecords)
|
||||||
|
if numberOfMAR > math.MaxUint16 {
|
||||||
|
return fmt.Errorf(
|
||||||
|
"%d multicast address records added, but the maximum is 65535",
|
||||||
|
numberOfMAR)
|
||||||
|
}
|
||||||
|
|
||||||
|
m.NumberOfMulticastAddressRecords = uint16(numberOfMAR)
|
||||||
|
}
|
||||||
|
|
||||||
|
buf, err := b.PrependBytes(4)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
copy(buf[0:2], []byte{0x0, 0x0})
|
||||||
|
binary.BigEndian.PutUint16(buf[2:4], m.NumberOfMulticastAddressRecords)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sums this layer up nicely formatted
|
||||||
|
func (m *MLDv2MulticastListenerReportMessage) String() string {
|
||||||
|
return fmt.Sprintf(
|
||||||
|
"Number of Mcast Addr Records: %d (actual %d), Multicast Address Records: %+v",
|
||||||
|
m.NumberOfMulticastAddressRecords,
|
||||||
|
len(m.MulticastAddressRecords),
|
||||||
|
m.MulticastAddressRecords)
|
||||||
|
}
|
||||||
|
|
||||||
|
// LayerType returns LayerTypeMLDv2MulticastListenerQuery.
|
||||||
|
func (*MLDv2MulticastListenerReportMessage) LayerType() gopacket.LayerType {
|
||||||
|
return LayerTypeMLDv2MulticastListenerReport
|
||||||
|
}
|
||||||
|
|
||||||
|
// CanDecode returns the set of layer types that this DecodingLayer can decode.
|
||||||
|
func (*MLDv2MulticastListenerReportMessage) CanDecode() gopacket.LayerClass {
|
||||||
|
return LayerTypeMLDv2MulticastListenerReport
|
||||||
|
}
|
||||||
|
|
||||||
|
// NextLayerType returns the layer type contained by this DecodingLayer.
|
||||||
|
func (*MLDv2MulticastListenerReportMessage) NextLayerType() gopacket.LayerType {
|
||||||
|
return gopacket.LayerTypePayload
|
||||||
|
}
|
||||||
|
|
||||||
|
// MLDv2MulticastAddressRecordType holds the type of a
|
||||||
|
// Multicast Address Record, according to
|
||||||
|
// https://tools.ietf.org/html/rfc3810#section-5.2.5 and
|
||||||
|
// https://tools.ietf.org/html/rfc3810#section-5.2.12
|
||||||
|
type MLDv2MulticastAddressRecordType uint8
|
||||||
|
|
||||||
|
const (
|
||||||
|
// MLDv2MulticastAddressRecordTypeModeIsIncluded stands for
|
||||||
|
// MODE_IS_INCLUDE - indicates that the interface has a filter
|
||||||
|
// mode of INCLUDE for the specified multicast address.
|
||||||
|
MLDv2MulticastAddressRecordTypeModeIsIncluded MLDv2MulticastAddressRecordType = 1
|
||||||
|
// MLDv2MulticastAddressRecordTypeModeIsExcluded stands for
|
||||||
|
// MODE_IS_EXCLUDE - indicates that the interface has a filter
|
||||||
|
// mode of EXCLUDE for the specified multicast address.
|
||||||
|
MLDv2MulticastAddressRecordTypeModeIsExcluded MLDv2MulticastAddressRecordType = 2
|
||||||
|
// MLDv2MulticastAddressRecordTypeChangeToIncludeMode stands for
|
||||||
|
// CHANGE_TO_INCLUDE_MODE - indicates that the interface has
|
||||||
|
// changed to INCLUDE filter mode for the specified multicast
|
||||||
|
// address.
|
||||||
|
MLDv2MulticastAddressRecordTypeChangeToIncludeMode MLDv2MulticastAddressRecordType = 3
|
||||||
|
// MLDv2MulticastAddressRecordTypeChangeToExcludeMode stands for
|
||||||
|
// CHANGE_TO_EXCLUDE_MODE - indicates that the interface has
|
||||||
|
// changed to EXCLUDE filter mode for the specified multicast
|
||||||
|
// address
|
||||||
|
MLDv2MulticastAddressRecordTypeChangeToExcludeMode MLDv2MulticastAddressRecordType = 4
|
||||||
|
// MLDv2MulticastAddressRecordTypeAllowNewSources stands for
|
||||||
|
// ALLOW_NEW_SOURCES - indicates that the Source Address [i]
|
||||||
|
// fields in this Multicast Address Record contain a list of
|
||||||
|
// the additional sources that the node wishes to listen to,
|
||||||
|
// for packets sent to the specified multicast address.
|
||||||
|
MLDv2MulticastAddressRecordTypeAllowNewSources MLDv2MulticastAddressRecordType = 5
|
||||||
|
// MLDv2MulticastAddressRecordTypeBlockOldSources stands for
|
||||||
|
// BLOCK_OLD_SOURCES - indicates that the Source Address [i]
|
||||||
|
// fields in this Multicast Address Record contain a list of
|
||||||
|
// the sources that the node no longer wishes to listen to,
|
||||||
|
// for packets sent to the specified multicast address.
|
||||||
|
MLDv2MulticastAddressRecordTypeBlockOldSources MLDv2MulticastAddressRecordType = 6
|
||||||
|
)
|
||||||
|
|
||||||
|
// Human readable record types
|
||||||
|
// Naming follows https://tools.ietf.org/html/rfc3810#section-5.2.12
|
||||||
|
func (m MLDv2MulticastAddressRecordType) String() string {
|
||||||
|
switch m {
|
||||||
|
case MLDv2MulticastAddressRecordTypeModeIsIncluded:
|
||||||
|
return "MODE_IS_INCLUDE"
|
||||||
|
case MLDv2MulticastAddressRecordTypeModeIsExcluded:
|
||||||
|
return "MODE_IS_EXCLUDE"
|
||||||
|
case MLDv2MulticastAddressRecordTypeChangeToIncludeMode:
|
||||||
|
return "CHANGE_TO_INCLUDE_MODE"
|
||||||
|
case MLDv2MulticastAddressRecordTypeChangeToExcludeMode:
|
||||||
|
return "CHANGE_TO_EXCLUDE_MODE"
|
||||||
|
case MLDv2MulticastAddressRecordTypeAllowNewSources:
|
||||||
|
return "ALLOW_NEW_SOURCES"
|
||||||
|
case MLDv2MulticastAddressRecordTypeBlockOldSources:
|
||||||
|
return "BLOCK_OLD_SOURCES"
|
||||||
|
default:
|
||||||
|
return fmt.Sprintf("UNKNOWN(%d)", m)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MLDv2MulticastAddressRecord contains information on the sender listening to a
|
||||||
|
// single multicast address on the interface the report is sent.
|
||||||
|
// https://tools.ietf.org/html/rfc3810#section-5.2.4
|
||||||
|
type MLDv2MulticastAddressRecord struct {
|
||||||
|
// 5.2.5. Record Type
|
||||||
|
RecordType MLDv2MulticastAddressRecordType
|
||||||
|
// 5.2.6. Auxiliary Data Length (number of 32-bit words)
|
||||||
|
AuxDataLen uint8
|
||||||
|
// 5.2.7. Number Of Sources (N)
|
||||||
|
N uint16
|
||||||
|
// 5.2.8. Multicast Address
|
||||||
|
MulticastAddress net.IP
|
||||||
|
// 5.2.9 Source Address [i]
|
||||||
|
SourceAddresses []net.IP
|
||||||
|
// 5.2.10 Auxiliary Data
|
||||||
|
AuxiliaryData []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
// decodes a multicast address record from bytes
|
||||||
|
func (m *MLDv2MulticastAddressRecord) decode(data []byte, df gopacket.DecodeFeedback) (int, error) {
|
||||||
|
if len(data) < 20 {
|
||||||
|
df.SetTruncated()
|
||||||
|
return 0, errors.New(
|
||||||
|
"Multicast Listener Report Message V2 layer less than 4 bytes for Multicast Address Record")
|
||||||
|
}
|
||||||
|
|
||||||
|
m.RecordType = MLDv2MulticastAddressRecordType(data[0])
|
||||||
|
m.AuxDataLen = data[1]
|
||||||
|
m.N = binary.BigEndian.Uint16(data[2:4])
|
||||||
|
m.MulticastAddress = data[4:20]
|
||||||
|
|
||||||
|
for i := uint16(0); i < m.N; i++ {
|
||||||
|
begin := 20 + (int(i) * 16)
|
||||||
|
end := begin + 16
|
||||||
|
|
||||||
|
if len(data) < end {
|
||||||
|
df.SetTruncated()
|
||||||
|
return begin, fmt.Errorf(
|
||||||
|
"Multicast Listener Report Message V2 layer less than %d bytes for Multicast Address Record", end)
|
||||||
|
}
|
||||||
|
|
||||||
|
m.SourceAddresses = append(m.SourceAddresses, data[begin:end])
|
||||||
|
}
|
||||||
|
|
||||||
|
expectedLengthWithouAuxData := 20 + (int(m.N) * 16)
|
||||||
|
expectedTotalLength := (int(m.AuxDataLen) * 4) + expectedLengthWithouAuxData // *4 because AuxDataLen are 32bit words
|
||||||
|
if len(data) < expectedTotalLength {
|
||||||
|
return expectedLengthWithouAuxData, fmt.Errorf(
|
||||||
|
"Multicast Listener Report Message V2 layer less than %d bytes for Multicast Address Record",
|
||||||
|
expectedLengthWithouAuxData)
|
||||||
|
}
|
||||||
|
|
||||||
|
m.AuxiliaryData = data[expectedLengthWithouAuxData:expectedTotalLength]
|
||||||
|
|
||||||
|
return expectedTotalLength, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// String sums this layer up nicely formatted
|
||||||
|
func (m *MLDv2MulticastAddressRecord) String() string {
|
||||||
|
return fmt.Sprintf(
|
||||||
|
"RecordType: %d (%s), AuxDataLen: %d [32-bit words], N: %d, Multicast Address: %s, SourceAddresses: %s, Auxiliary Data: %#x",
|
||||||
|
m.RecordType,
|
||||||
|
m.RecordType.String(),
|
||||||
|
m.AuxDataLen,
|
||||||
|
m.N,
|
||||||
|
m.MulticastAddress.To16(),
|
||||||
|
m.SourceAddresses,
|
||||||
|
m.AuxiliaryData)
|
||||||
|
}
|
||||||
|
|
||||||
|
// serializes a multicast address record
|
||||||
|
func (m *MLDv2MulticastAddressRecord) serializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
|
||||||
|
if err := m.serializeAuxiliaryDataTo(b, opts); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := m.serializeSourceAddressesTo(b, opts); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
buf, err := b.PrependBytes(20)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
buf[0] = uint8(m.RecordType)
|
||||||
|
buf[1] = m.AuxDataLen
|
||||||
|
binary.BigEndian.PutUint16(buf[2:4], m.N)
|
||||||
|
|
||||||
|
ma16 := m.MulticastAddress.To16()
|
||||||
|
if ma16 == nil {
|
||||||
|
return fmt.Errorf("invalid multicast address '%s'", m.MulticastAddress)
|
||||||
|
}
|
||||||
|
copy(buf[4:20], ma16)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// serializes the auxiliary data of a multicast address record
|
||||||
|
func (m *MLDv2MulticastAddressRecord) serializeAuxiliaryDataTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
|
||||||
|
if remainder := len(m.AuxiliaryData) % 4; remainder != 0 {
|
||||||
|
zeroWord := []byte{0x0, 0x0, 0x0, 0x0}
|
||||||
|
m.AuxiliaryData = append(m.AuxiliaryData, zeroWord[:remainder]...)
|
||||||
|
}
|
||||||
|
|
||||||
|
if opts.FixLengths {
|
||||||
|
auxDataLen := len(m.AuxiliaryData) / 4
|
||||||
|
|
||||||
|
if auxDataLen > math.MaxUint8 {
|
||||||
|
return fmt.Errorf("auxilary data is %d 32-bit words, but the maximum is 255 32-bit words", auxDataLen)
|
||||||
|
}
|
||||||
|
|
||||||
|
m.AuxDataLen = uint8(auxDataLen)
|
||||||
|
}
|
||||||
|
|
||||||
|
buf, err := b.PrependBytes(len(m.AuxiliaryData))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
copy(buf, m.AuxiliaryData)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// serializes the source addresses of a multicast address record preserving the order
|
||||||
|
func (m *MLDv2MulticastAddressRecord) serializeSourceAddressesTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
|
||||||
|
if opts.FixLengths {
|
||||||
|
numberOfSourceAddresses := len(m.SourceAddresses)
|
||||||
|
|
||||||
|
if numberOfSourceAddresses > math.MaxUint16 {
|
||||||
|
return fmt.Errorf(
|
||||||
|
"%d source addresses added, but the maximum is 65535",
|
||||||
|
numberOfSourceAddresses)
|
||||||
|
}
|
||||||
|
|
||||||
|
m.N = uint16(numberOfSourceAddresses)
|
||||||
|
}
|
||||||
|
|
||||||
|
lastItemIdx := len(m.SourceAddresses) - 1
|
||||||
|
for k := range m.SourceAddresses {
|
||||||
|
i := lastItemIdx - k // reverse order
|
||||||
|
|
||||||
|
buf, err := b.PrependBytes(16)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
sa16 := m.SourceAddresses[i].To16()
|
||||||
|
if sa16 == nil {
|
||||||
|
return fmt.Errorf("invalid source address [%d] '%s'", i, m.SourceAddresses[i])
|
||||||
|
}
|
||||||
|
copy(buf, sa16)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func decodeMLDv2MulticastListenerReport(data []byte, p gopacket.PacketBuilder) error {
|
||||||
|
m := &MLDv2MulticastListenerReportMessage{}
|
||||||
|
return decodingLayerDecoder(m, data, p)
|
||||||
|
}
|
||||||
|
|
||||||
|
func decodeMLDv2MulticastListenerQuery(data []byte, p gopacket.PacketBuilder) error {
|
||||||
|
m := &MLDv2MulticastListenerQueryMessage{}
|
||||||
|
return decodingLayerDecoder(m, data, p)
|
||||||
|
}
|
|
@ -0,0 +1,150 @@
|
||||||
|
// Copyright 2018, The GoPacket Authors, All rights reserved.
|
||||||
|
//
|
||||||
|
// Use of this source code is governed by a BSD-style license
|
||||||
|
// that can be found in the LICENSE file in the root of the source
|
||||||
|
// tree.
|
||||||
|
//
|
||||||
|
//******************************************************************************
|
||||||
|
|
||||||
|
package layers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/binary"
|
||||||
|
"errors"
|
||||||
|
"github.com/google/gopacket"
|
||||||
|
)
|
||||||
|
|
||||||
|
//******************************************************************************
|
||||||
|
//
|
||||||
|
// ModbusTCP Decoding Layer
|
||||||
|
// ------------------------------------------
|
||||||
|
// This file provides a GoPacket decoding layer for ModbusTCP.
|
||||||
|
//
|
||||||
|
//******************************************************************************
|
||||||
|
|
||||||
|
const mbapRecordSizeInBytes int = 7
|
||||||
|
const modbusPDUMinimumRecordSizeInBytes int = 2
|
||||||
|
const modbusPDUMaximumRecordSizeInBytes int = 253
|
||||||
|
|
||||||
|
// ModbusProtocol type
|
||||||
|
type ModbusProtocol uint16
|
||||||
|
|
||||||
|
// ModbusProtocol known values.
|
||||||
|
const (
|
||||||
|
ModbusProtocolModbus ModbusProtocol = 0
|
||||||
|
)
|
||||||
|
|
||||||
|
func (mp ModbusProtocol) String() string {
|
||||||
|
switch mp {
|
||||||
|
default:
|
||||||
|
return "Unknown"
|
||||||
|
case ModbusProtocolModbus:
|
||||||
|
return "Modbus"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//******************************************************************************
|
||||||
|
|
||||||
|
// ModbusTCP Type
|
||||||
|
// --------
|
||||||
|
// Type ModbusTCP implements the DecodingLayer interface. Each ModbusTCP object
|
||||||
|
// represents in a structured form the MODBUS Application Protocol header (MBAP) record present as the TCP
|
||||||
|
// payload in an ModbusTCP TCP packet.
|
||||||
|
//
|
||||||
|
type ModbusTCP struct {
|
||||||
|
BaseLayer // Stores the packet bytes and payload (Modbus PDU) bytes .
|
||||||
|
|
||||||
|
TransactionIdentifier uint16 // Identification of a MODBUS Request/Response transaction
|
||||||
|
ProtocolIdentifier ModbusProtocol // It is used for intra-system multiplexing
|
||||||
|
Length uint16 // Number of following bytes (includes 1 byte for UnitIdentifier + Modbus data length
|
||||||
|
UnitIdentifier uint8 // Identification of a remote slave connected on a serial line or on other buses
|
||||||
|
}
|
||||||
|
|
||||||
|
//******************************************************************************
|
||||||
|
|
||||||
|
// LayerType returns the layer type of the ModbusTCP object, which is LayerTypeModbusTCP.
|
||||||
|
func (d *ModbusTCP) LayerType() gopacket.LayerType {
|
||||||
|
return LayerTypeModbusTCP
|
||||||
|
}
|
||||||
|
|
||||||
|
//******************************************************************************
|
||||||
|
|
||||||
|
// decodeModbusTCP analyses a byte slice and attempts to decode it as an ModbusTCP
|
||||||
|
// record of a TCP packet.
|
||||||
|
//
|
||||||
|
// If it succeeds, it loads p with information about the packet and returns nil.
|
||||||
|
// If it fails, it returns an error (non nil).
|
||||||
|
//
|
||||||
|
// This function is employed in layertypes.go to register the ModbusTCP layer.
|
||||||
|
func decodeModbusTCP(data []byte, p gopacket.PacketBuilder) error {
|
||||||
|
|
||||||
|
// Attempt to decode the byte slice.
|
||||||
|
d := &ModbusTCP{}
|
||||||
|
err := d.DecodeFromBytes(data, p)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// If the decoding worked, add the layer to the packet and set it
|
||||||
|
// as the application layer too, if there isn't already one.
|
||||||
|
p.AddLayer(d)
|
||||||
|
p.SetApplicationLayer(d)
|
||||||
|
|
||||||
|
return p.NextDecoder(d.NextLayerType())
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//******************************************************************************
|
||||||
|
|
||||||
|
// DecodeFromBytes analyses a byte slice and attempts to decode it as an ModbusTCP
|
||||||
|
// record of a TCP packet.
|
||||||
|
//
|
||||||
|
// Upon succeeds, it loads the ModbusTCP object with information about the packet
|
||||||
|
// and returns nil.
|
||||||
|
// Upon failure, it returns an error (non nil).
|
||||||
|
func (d *ModbusTCP) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
|
||||||
|
|
||||||
|
// If the data block is too short to be a MBAP record, then return an error.
|
||||||
|
if len(data) < mbapRecordSizeInBytes+modbusPDUMinimumRecordSizeInBytes {
|
||||||
|
df.SetTruncated()
|
||||||
|
return errors.New("ModbusTCP packet too short")
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(data) > mbapRecordSizeInBytes+modbusPDUMaximumRecordSizeInBytes {
|
||||||
|
df.SetTruncated()
|
||||||
|
return errors.New("ModbusTCP packet too long")
|
||||||
|
}
|
||||||
|
|
||||||
|
// ModbusTCP type embeds type BaseLayer which contains two fields:
|
||||||
|
// Contents is supposed to contain the bytes of the data at this level (MPBA).
|
||||||
|
// Payload is supposed to contain the payload of this level (PDU).
|
||||||
|
d.BaseLayer = BaseLayer{Contents: data[:mbapRecordSizeInBytes], Payload: data[mbapRecordSizeInBytes:len(data)]}
|
||||||
|
|
||||||
|
// Extract the fields from the block of bytes.
|
||||||
|
// The fields can just be copied in big endian order.
|
||||||
|
d.TransactionIdentifier = binary.BigEndian.Uint16(data[:2])
|
||||||
|
d.ProtocolIdentifier = ModbusProtocol(binary.BigEndian.Uint16(data[2:4]))
|
||||||
|
d.Length = binary.BigEndian.Uint16(data[4:6])
|
||||||
|
|
||||||
|
// Length should have the size of the payload plus one byte (size of UnitIdentifier)
|
||||||
|
if d.Length != uint16(len(d.BaseLayer.Payload)+1) {
|
||||||
|
df.SetTruncated()
|
||||||
|
return errors.New("ModbusTCP packet with wrong field value (Length)")
|
||||||
|
}
|
||||||
|
d.UnitIdentifier = uint8(data[6])
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
//******************************************************************************
|
||||||
|
|
||||||
|
// NextLayerType returns the layer type of the ModbusTCP payload, which is LayerTypePayload.
|
||||||
|
func (d *ModbusTCP) NextLayerType() gopacket.LayerType {
|
||||||
|
return gopacket.LayerTypePayload
|
||||||
|
}
|
||||||
|
|
||||||
|
//******************************************************************************
|
||||||
|
|
||||||
|
// Payload returns Modbus Protocol Data Unit (PDU) composed by Function Code and Data, it is carried within ModbusTCP packets
|
||||||
|
func (d *ModbusTCP) Payload() []byte {
|
||||||
|
return d.BaseLayer.Payload
|
||||||
|
}
|
|
@ -0,0 +1,87 @@
|
||||||
|
// 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"
|
||||||
|
)
|
||||||
|
|
||||||
|
// MPLS is the MPLS packet header.
|
||||||
|
type MPLS struct {
|
||||||
|
BaseLayer
|
||||||
|
Label uint32
|
||||||
|
TrafficClass uint8
|
||||||
|
StackBottom bool
|
||||||
|
TTL uint8
|
||||||
|
}
|
||||||
|
|
||||||
|
// LayerType returns gopacket.LayerTypeMPLS.
|
||||||
|
func (m *MPLS) LayerType() gopacket.LayerType { return LayerTypeMPLS }
|
||||||
|
|
||||||
|
// ProtocolGuessingDecoder attempts to guess the protocol of the bytes it's
|
||||||
|
// given, then decode the packet accordingly. Its algorithm for guessing is:
|
||||||
|
// If the packet starts with byte 0x45-0x4F: IPv4
|
||||||
|
// If the packet starts with byte 0x60-0x6F: IPv6
|
||||||
|
// Otherwise: Error
|
||||||
|
// See draft-hsmit-isis-aal5mux-00.txt for more detail on this approach.
|
||||||
|
type ProtocolGuessingDecoder struct{}
|
||||||
|
|
||||||
|
func (ProtocolGuessingDecoder) Decode(data []byte, p gopacket.PacketBuilder) error {
|
||||||
|
switch data[0] {
|
||||||
|
// 0x40 | header_len, where header_len is at least 5.
|
||||||
|
case 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f:
|
||||||
|
return decodeIPv4(data, p)
|
||||||
|
// IPv6 can start with any byte whose first 4 bits are 0x6.
|
||||||
|
case 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f:
|
||||||
|
return decodeIPv6(data, p)
|
||||||
|
}
|
||||||
|
return errors.New("Unable to guess protocol of packet data")
|
||||||
|
}
|
||||||
|
|
||||||
|
// MPLSPayloadDecoder is the decoder used to data encapsulated by each MPLS
|
||||||
|
// layer. MPLS contains no type information, so we have to explicitly decide
|
||||||
|
// which decoder to use. This is initially set to ProtocolGuessingDecoder, our
|
||||||
|
// simple attempt at guessing protocols based on the first few bytes of data
|
||||||
|
// available to us. However, if you know that in your environment MPLS always
|
||||||
|
// encapsulates a specific protocol, you may reset this.
|
||||||
|
var MPLSPayloadDecoder gopacket.Decoder = ProtocolGuessingDecoder{}
|
||||||
|
|
||||||
|
func decodeMPLS(data []byte, p gopacket.PacketBuilder) error {
|
||||||
|
decoded := binary.BigEndian.Uint32(data[:4])
|
||||||
|
mpls := &MPLS{
|
||||||
|
Label: decoded >> 12,
|
||||||
|
TrafficClass: uint8(decoded>>9) & 0x7,
|
||||||
|
StackBottom: decoded&0x100 != 0,
|
||||||
|
TTL: uint8(decoded),
|
||||||
|
BaseLayer: BaseLayer{data[:4], data[4:]},
|
||||||
|
}
|
||||||
|
p.AddLayer(mpls)
|
||||||
|
if mpls.StackBottom {
|
||||||
|
return p.NextDecoder(MPLSPayloadDecoder)
|
||||||
|
}
|
||||||
|
return p.NextDecoder(gopacket.DecodeFunc(decodeMPLS))
|
||||||
|
}
|
||||||
|
|
||||||
|
// SerializeTo writes the serialized form of this layer into the
|
||||||
|
// SerializationBuffer, implementing gopacket.SerializableLayer.
|
||||||
|
// See the docs for gopacket.SerializableLayer for more info.
|
||||||
|
func (m *MPLS) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
|
||||||
|
bytes, err := b.PrependBytes(4)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
encoded := m.Label << 12
|
||||||
|
encoded |= uint32(m.TrafficClass) << 9
|
||||||
|
encoded |= uint32(m.TTL)
|
||||||
|
if m.StackBottom {
|
||||||
|
encoded |= 0x100
|
||||||
|
}
|
||||||
|
binary.BigEndian.PutUint32(bytes, encoded)
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,611 @@
|
||||||
|
// 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://anonsvn.wireshark.org/wireshark/trunk/epan/dissectors/packet-ndp.c
|
||||||
|
|
||||||
|
package layers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/google/gopacket"
|
||||||
|
"net"
|
||||||
|
)
|
||||||
|
|
||||||
|
type NDPChassisType uint8
|
||||||
|
|
||||||
|
// Nortel Chassis Types
|
||||||
|
const (
|
||||||
|
NDPChassisother NDPChassisType = 1
|
||||||
|
NDPChassis3000 NDPChassisType = 2
|
||||||
|
NDPChassis3030 NDPChassisType = 3
|
||||||
|
NDPChassis2310 NDPChassisType = 4
|
||||||
|
NDPChassis2810 NDPChassisType = 5
|
||||||
|
NDPChassis2912 NDPChassisType = 6
|
||||||
|
NDPChassis2914 NDPChassisType = 7
|
||||||
|
NDPChassis271x NDPChassisType = 8
|
||||||
|
NDPChassis2813 NDPChassisType = 9
|
||||||
|
NDPChassis2814 NDPChassisType = 10
|
||||||
|
NDPChassis2915 NDPChassisType = 11
|
||||||
|
NDPChassis5000 NDPChassisType = 12
|
||||||
|
NDPChassis2813SA NDPChassisType = 13
|
||||||
|
NDPChassis2814SA NDPChassisType = 14
|
||||||
|
NDPChassis810M NDPChassisType = 15
|
||||||
|
NDPChassisEthercell NDPChassisType = 16
|
||||||
|
NDPChassis5005 NDPChassisType = 17
|
||||||
|
NDPChassisAlcatelEWC NDPChassisType = 18
|
||||||
|
NDPChassis2715SA NDPChassisType = 20
|
||||||
|
NDPChassis2486 NDPChassisType = 21
|
||||||
|
NDPChassis28000series NDPChassisType = 22
|
||||||
|
NDPChassis23000series NDPChassisType = 23
|
||||||
|
NDPChassis5DN00xseries NDPChassisType = 24
|
||||||
|
NDPChassisBayStackEthernet NDPChassisType = 25
|
||||||
|
NDPChassis23100series NDPChassisType = 26
|
||||||
|
NDPChassis100BaseTHub NDPChassisType = 27
|
||||||
|
NDPChassis3000FastEthernet NDPChassisType = 28
|
||||||
|
NDPChassisOrionSwitch NDPChassisType = 29
|
||||||
|
NDPChassisDDS NDPChassisType = 31
|
||||||
|
NDPChassisCentillion6slot NDPChassisType = 32
|
||||||
|
NDPChassisCentillion12slot NDPChassisType = 33
|
||||||
|
NDPChassisCentillion1slot NDPChassisType = 34
|
||||||
|
NDPChassisBayStack301 NDPChassisType = 35
|
||||||
|
NDPChassisBayStackTokenRingHub NDPChassisType = 36
|
||||||
|
NDPChassisFVCMultimediaSwitch NDPChassisType = 37
|
||||||
|
NDPChassisSwitchNode NDPChassisType = 38
|
||||||
|
NDPChassisBayStack302Switch NDPChassisType = 39
|
||||||
|
NDPChassisBayStack350Switch NDPChassisType = 40
|
||||||
|
NDPChassisBayStack150EthernetHub NDPChassisType = 41
|
||||||
|
NDPChassisCentillion50NSwitch NDPChassisType = 42
|
||||||
|
NDPChassisCentillion50TSwitch NDPChassisType = 43
|
||||||
|
NDPChassisBayStack303304Switches NDPChassisType = 44
|
||||||
|
NDPChassisBayStack200EthernetHub NDPChassisType = 45
|
||||||
|
NDPChassisBayStack25010100EthernetHub NDPChassisType = 46
|
||||||
|
NDPChassisBayStack450101001000Switches NDPChassisType = 48
|
||||||
|
NDPChassisBayStack41010100Switches NDPChassisType = 49
|
||||||
|
NDPChassisPassport1200L3Switch NDPChassisType = 50
|
||||||
|
NDPChassisPassport1250L3Switch NDPChassisType = 51
|
||||||
|
NDPChassisPassport1100L3Switch NDPChassisType = 52
|
||||||
|
NDPChassisPassport1150L3Switch NDPChassisType = 53
|
||||||
|
NDPChassisPassport1050L3Switch NDPChassisType = 54
|
||||||
|
NDPChassisPassport1051L3Switch NDPChassisType = 55
|
||||||
|
NDPChassisPassport8610L3Switch NDPChassisType = 56
|
||||||
|
NDPChassisPassport8606L3Switch NDPChassisType = 57
|
||||||
|
NDPChassisPassport8010 NDPChassisType = 58
|
||||||
|
NDPChassisPassport8006 NDPChassisType = 59
|
||||||
|
NDPChassisBayStack670wirelessaccesspoint NDPChassisType = 60
|
||||||
|
NDPChassisPassport740 NDPChassisType = 61
|
||||||
|
NDPChassisPassport750 NDPChassisType = 62
|
||||||
|
NDPChassisPassport790 NDPChassisType = 63
|
||||||
|
NDPChassisBusinessPolicySwitch200010100Switches NDPChassisType = 64
|
||||||
|
NDPChassisPassport8110L2Switch NDPChassisType = 65
|
||||||
|
NDPChassisPassport8106L2Switch NDPChassisType = 66
|
||||||
|
NDPChassisBayStack3580GigSwitch NDPChassisType = 67
|
||||||
|
NDPChassisBayStack10PowerSupplyUnit NDPChassisType = 68
|
||||||
|
NDPChassisBayStack42010100Switch NDPChassisType = 69
|
||||||
|
NDPChassisOPTeraMetro1200EthernetServiceModule NDPChassisType = 70
|
||||||
|
NDPChassisOPTera8010co NDPChassisType = 71
|
||||||
|
NDPChassisOPTera8610coL3Switch NDPChassisType = 72
|
||||||
|
NDPChassisOPTera8110coL2Switch NDPChassisType = 73
|
||||||
|
NDPChassisOPTera8003 NDPChassisType = 74
|
||||||
|
NDPChassisOPTera8603L3Switch NDPChassisType = 75
|
||||||
|
NDPChassisOPTera8103L2Switch NDPChassisType = 76
|
||||||
|
NDPChassisBayStack380101001000Switch NDPChassisType = 77
|
||||||
|
NDPChassisEthernetSwitch47048T NDPChassisType = 78
|
||||||
|
NDPChassisOPTeraMetro1450EthernetServiceModule NDPChassisType = 79
|
||||||
|
NDPChassisOPTeraMetro1400EthernetServiceModule NDPChassisType = 80
|
||||||
|
NDPChassisAlteonSwitchFamily NDPChassisType = 81
|
||||||
|
NDPChassisEthernetSwitch46024TPWR NDPChassisType = 82
|
||||||
|
NDPChassisOPTeraMetro8010OPML2Switch NDPChassisType = 83
|
||||||
|
NDPChassisOPTeraMetro8010coOPML2Switch NDPChassisType = 84
|
||||||
|
NDPChassisOPTeraMetro8006OPML2Switch NDPChassisType = 85
|
||||||
|
NDPChassisOPTeraMetro8003OPML2Switch NDPChassisType = 86
|
||||||
|
NDPChassisAlteon180e NDPChassisType = 87
|
||||||
|
NDPChassisAlteonAD3 NDPChassisType = 88
|
||||||
|
NDPChassisAlteon184 NDPChassisType = 89
|
||||||
|
NDPChassisAlteonAD4 NDPChassisType = 90
|
||||||
|
NDPChassisPassport1424L3Switch NDPChassisType = 91
|
||||||
|
NDPChassisPassport1648L3Switch NDPChassisType = 92
|
||||||
|
NDPChassisPassport1612L3Switch NDPChassisType = 93
|
||||||
|
NDPChassisPassport1624L3Switch NDPChassisType = 94
|
||||||
|
NDPChassisBayStack38024FFiber1000Switch NDPChassisType = 95
|
||||||
|
NDPChassisEthernetRoutingSwitch551024T NDPChassisType = 96
|
||||||
|
NDPChassisEthernetRoutingSwitch551048T NDPChassisType = 97
|
||||||
|
NDPChassisEthernetSwitch47024T NDPChassisType = 98
|
||||||
|
NDPChassisNortelNetworksWirelessLANAccessPoint2220 NDPChassisType = 99
|
||||||
|
NDPChassisPassportRBS2402L3Switch NDPChassisType = 100
|
||||||
|
NDPChassisAlteonApplicationSwitch2424 NDPChassisType = 101
|
||||||
|
NDPChassisAlteonApplicationSwitch2224 NDPChassisType = 102
|
||||||
|
NDPChassisAlteonApplicationSwitch2208 NDPChassisType = 103
|
||||||
|
NDPChassisAlteonApplicationSwitch2216 NDPChassisType = 104
|
||||||
|
NDPChassisAlteonApplicationSwitch3408 NDPChassisType = 105
|
||||||
|
NDPChassisAlteonApplicationSwitch3416 NDPChassisType = 106
|
||||||
|
NDPChassisNortelNetworksWirelessLANSecuritySwitch2250 NDPChassisType = 107
|
||||||
|
NDPChassisEthernetSwitch42548T NDPChassisType = 108
|
||||||
|
NDPChassisEthernetSwitch42524T NDPChassisType = 109
|
||||||
|
NDPChassisNortelNetworksWirelessLANAccessPoint2221 NDPChassisType = 110
|
||||||
|
NDPChassisNortelMetroEthernetServiceUnit24TSPFswitch NDPChassisType = 111
|
||||||
|
NDPChassisNortelMetroEthernetServiceUnit24TLXDCswitch NDPChassisType = 112
|
||||||
|
NDPChassisPassport830010slotchassis NDPChassisType = 113
|
||||||
|
NDPChassisPassport83006slotchassis NDPChassisType = 114
|
||||||
|
NDPChassisEthernetRoutingSwitch552024TPWR NDPChassisType = 115
|
||||||
|
NDPChassisEthernetRoutingSwitch552048TPWR NDPChassisType = 116
|
||||||
|
NDPChassisNortelNetworksVPNGateway3050 NDPChassisType = 117
|
||||||
|
NDPChassisAlteonSSL31010100 NDPChassisType = 118
|
||||||
|
NDPChassisAlteonSSL31010100Fiber NDPChassisType = 119
|
||||||
|
NDPChassisAlteonSSL31010100FIPS NDPChassisType = 120
|
||||||
|
NDPChassisAlteonSSL410101001000 NDPChassisType = 121
|
||||||
|
NDPChassisAlteonSSL410101001000Fiber NDPChassisType = 122
|
||||||
|
NDPChassisAlteonApplicationSwitch2424SSL NDPChassisType = 123
|
||||||
|
NDPChassisEthernetSwitch32524T NDPChassisType = 124
|
||||||
|
NDPChassisEthernetSwitch32524G NDPChassisType = 125
|
||||||
|
NDPChassisNortelNetworksWirelessLANAccessPoint2225 NDPChassisType = 126
|
||||||
|
NDPChassisNortelNetworksWirelessLANSecuritySwitch2270 NDPChassisType = 127
|
||||||
|
NDPChassis24portEthernetSwitch47024TPWR NDPChassisType = 128
|
||||||
|
NDPChassis48portEthernetSwitch47048TPWR NDPChassisType = 129
|
||||||
|
NDPChassisEthernetRoutingSwitch553024TFD NDPChassisType = 130
|
||||||
|
NDPChassisEthernetSwitch351024T NDPChassisType = 131
|
||||||
|
NDPChassisNortelMetroEthernetServiceUnit12GACL3Switch NDPChassisType = 132
|
||||||
|
NDPChassisNortelMetroEthernetServiceUnit12GDCL3Switch NDPChassisType = 133
|
||||||
|
NDPChassisNortelSecureAccessSwitch NDPChassisType = 134
|
||||||
|
NDPChassisNortelNetworksVPNGateway3070 NDPChassisType = 135
|
||||||
|
NDPChassisOPTeraMetro3500 NDPChassisType = 136
|
||||||
|
NDPChassisSMBBES101024T NDPChassisType = 137
|
||||||
|
NDPChassisSMBBES101048T NDPChassisType = 138
|
||||||
|
NDPChassisSMBBES102024TPWR NDPChassisType = 139
|
||||||
|
NDPChassisSMBBES102048TPWR NDPChassisType = 140
|
||||||
|
NDPChassisSMBBES201024T NDPChassisType = 141
|
||||||
|
NDPChassisSMBBES201048T NDPChassisType = 142
|
||||||
|
NDPChassisSMBBES202024TPWR NDPChassisType = 143
|
||||||
|
NDPChassisSMBBES202048TPWR NDPChassisType = 144
|
||||||
|
NDPChassisSMBBES11024T NDPChassisType = 145
|
||||||
|
NDPChassisSMBBES11048T NDPChassisType = 146
|
||||||
|
NDPChassisSMBBES12024TPWR NDPChassisType = 147
|
||||||
|
NDPChassisSMBBES12048TPWR NDPChassisType = 148
|
||||||
|
NDPChassisSMBBES21024T NDPChassisType = 149
|
||||||
|
NDPChassisSMBBES21048T NDPChassisType = 150
|
||||||
|
NDPChassisSMBBES22024TPWR NDPChassisType = 151
|
||||||
|
NDPChassisSMBBES22048TPWR NDPChassisType = 152
|
||||||
|
NDPChassisOME6500 NDPChassisType = 153
|
||||||
|
NDPChassisEthernetRoutingSwitch4548GT NDPChassisType = 154
|
||||||
|
NDPChassisEthernetRoutingSwitch4548GTPWR NDPChassisType = 155
|
||||||
|
NDPChassisEthernetRoutingSwitch4550T NDPChassisType = 156
|
||||||
|
NDPChassisEthernetRoutingSwitch4550TPWR NDPChassisType = 157
|
||||||
|
NDPChassisEthernetRoutingSwitch4526FX NDPChassisType = 158
|
||||||
|
NDPChassisEthernetRoutingSwitch250026T NDPChassisType = 159
|
||||||
|
NDPChassisEthernetRoutingSwitch250026TPWR NDPChassisType = 160
|
||||||
|
NDPChassisEthernetRoutingSwitch250050T NDPChassisType = 161
|
||||||
|
NDPChassisEthernetRoutingSwitch250050TPWR NDPChassisType = 162
|
||||||
|
)
|
||||||
|
|
||||||
|
type NDPBackplaneType uint8
|
||||||
|
|
||||||
|
// Nortel Backplane Types
|
||||||
|
const (
|
||||||
|
NDPBackplaneOther NDPBackplaneType = 1
|
||||||
|
NDPBackplaneEthernet NDPBackplaneType = 2
|
||||||
|
NDPBackplaneEthernetTokenring NDPBackplaneType = 3
|
||||||
|
NDPBackplaneEthernetFDDI NDPBackplaneType = 4
|
||||||
|
NDPBackplaneEthernetTokenringFDDI NDPBackplaneType = 5
|
||||||
|
NDPBackplaneEthernetTokenringRedundantPower NDPBackplaneType = 6
|
||||||
|
NDPBackplaneEthernetTokenringFDDIRedundantPower NDPBackplaneType = 7
|
||||||
|
NDPBackplaneTokenRing NDPBackplaneType = 8
|
||||||
|
NDPBackplaneEthernetTokenringFastEthernet NDPBackplaneType = 9
|
||||||
|
NDPBackplaneEthernetFastEthernet NDPBackplaneType = 10
|
||||||
|
NDPBackplaneEthernetTokenringFastEthernetRedundantPower NDPBackplaneType = 11
|
||||||
|
NDPBackplaneEthernetFastEthernetGigabitEthernet NDPBackplaneType = 12
|
||||||
|
)
|
||||||
|
|
||||||
|
type NDPState uint8
|
||||||
|
|
||||||
|
// Device State
|
||||||
|
const (
|
||||||
|
NDPStateTopology NDPState = 1
|
||||||
|
NDPStateHeartbeat NDPState = 2
|
||||||
|
NDPStateNew NDPState = 3
|
||||||
|
)
|
||||||
|
|
||||||
|
// NortelDiscovery is a packet layer containing the Nortel Discovery Protocol.
|
||||||
|
type NortelDiscovery struct {
|
||||||
|
BaseLayer
|
||||||
|
IPAddress net.IP
|
||||||
|
SegmentID []byte
|
||||||
|
Chassis NDPChassisType
|
||||||
|
Backplane NDPBackplaneType
|
||||||
|
State NDPState
|
||||||
|
NumLinks uint8
|
||||||
|
}
|
||||||
|
|
||||||
|
// LayerType returns gopacket.LayerTypeNortelDiscovery.
|
||||||
|
func (c *NortelDiscovery) LayerType() gopacket.LayerType {
|
||||||
|
return LayerTypeNortelDiscovery
|
||||||
|
}
|
||||||
|
|
||||||
|
func decodeNortelDiscovery(data []byte, p gopacket.PacketBuilder) error {
|
||||||
|
c := &NortelDiscovery{}
|
||||||
|
if len(data) < 11 {
|
||||||
|
return fmt.Errorf("Invalid NortelDiscovery packet length %d", len(data))
|
||||||
|
}
|
||||||
|
c.IPAddress = data[0:4]
|
||||||
|
c.SegmentID = data[4:7]
|
||||||
|
c.Chassis = NDPChassisType(data[7])
|
||||||
|
c.Backplane = NDPBackplaneType(data[8])
|
||||||
|
c.State = NDPState(data[9])
|
||||||
|
c.NumLinks = uint8(data[10])
|
||||||
|
p.AddLayer(c)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t NDPChassisType) String() (s string) {
|
||||||
|
switch t {
|
||||||
|
case NDPChassisother:
|
||||||
|
s = "other"
|
||||||
|
case NDPChassis3000:
|
||||||
|
s = "3000"
|
||||||
|
case NDPChassis3030:
|
||||||
|
s = "3030"
|
||||||
|
case NDPChassis2310:
|
||||||
|
s = "2310"
|
||||||
|
case NDPChassis2810:
|
||||||
|
s = "2810"
|
||||||
|
case NDPChassis2912:
|
||||||
|
s = "2912"
|
||||||
|
case NDPChassis2914:
|
||||||
|
s = "2914"
|
||||||
|
case NDPChassis271x:
|
||||||
|
s = "271x"
|
||||||
|
case NDPChassis2813:
|
||||||
|
s = "2813"
|
||||||
|
case NDPChassis2814:
|
||||||
|
s = "2814"
|
||||||
|
case NDPChassis2915:
|
||||||
|
s = "2915"
|
||||||
|
case NDPChassis5000:
|
||||||
|
s = "5000"
|
||||||
|
case NDPChassis2813SA:
|
||||||
|
s = "2813SA"
|
||||||
|
case NDPChassis2814SA:
|
||||||
|
s = "2814SA"
|
||||||
|
case NDPChassis810M:
|
||||||
|
s = "810M"
|
||||||
|
case NDPChassisEthercell:
|
||||||
|
s = "Ethercell"
|
||||||
|
case NDPChassis5005:
|
||||||
|
s = "5005"
|
||||||
|
case NDPChassisAlcatelEWC:
|
||||||
|
s = "Alcatel Ethernet workgroup conc."
|
||||||
|
case NDPChassis2715SA:
|
||||||
|
s = "2715SA"
|
||||||
|
case NDPChassis2486:
|
||||||
|
s = "2486"
|
||||||
|
case NDPChassis28000series:
|
||||||
|
s = "28000 series"
|
||||||
|
case NDPChassis23000series:
|
||||||
|
s = "23000 series"
|
||||||
|
case NDPChassis5DN00xseries:
|
||||||
|
s = "5DN00x series"
|
||||||
|
case NDPChassisBayStackEthernet:
|
||||||
|
s = "BayStack Ethernet"
|
||||||
|
case NDPChassis23100series:
|
||||||
|
s = "23100 series"
|
||||||
|
case NDPChassis100BaseTHub:
|
||||||
|
s = "100Base-T Hub"
|
||||||
|
case NDPChassis3000FastEthernet:
|
||||||
|
s = "3000 Fast Ethernet"
|
||||||
|
case NDPChassisOrionSwitch:
|
||||||
|
s = "Orion switch"
|
||||||
|
case NDPChassisDDS:
|
||||||
|
s = "DDS"
|
||||||
|
case NDPChassisCentillion6slot:
|
||||||
|
s = "Centillion (6 slot)"
|
||||||
|
case NDPChassisCentillion12slot:
|
||||||
|
s = "Centillion (12 slot)"
|
||||||
|
case NDPChassisCentillion1slot:
|
||||||
|
s = "Centillion (1 slot)"
|
||||||
|
case NDPChassisBayStack301:
|
||||||
|
s = "BayStack 301"
|
||||||
|
case NDPChassisBayStackTokenRingHub:
|
||||||
|
s = "BayStack TokenRing Hub"
|
||||||
|
case NDPChassisFVCMultimediaSwitch:
|
||||||
|
s = "FVC Multimedia Switch"
|
||||||
|
case NDPChassisSwitchNode:
|
||||||
|
s = "Switch Node"
|
||||||
|
case NDPChassisBayStack302Switch:
|
||||||
|
s = "BayStack 302 Switch"
|
||||||
|
case NDPChassisBayStack350Switch:
|
||||||
|
s = "BayStack 350 Switch"
|
||||||
|
case NDPChassisBayStack150EthernetHub:
|
||||||
|
s = "BayStack 150 Ethernet Hub"
|
||||||
|
case NDPChassisCentillion50NSwitch:
|
||||||
|
s = "Centillion 50N switch"
|
||||||
|
case NDPChassisCentillion50TSwitch:
|
||||||
|
s = "Centillion 50T switch"
|
||||||
|
case NDPChassisBayStack303304Switches:
|
||||||
|
s = "BayStack 303 and 304 Switches"
|
||||||
|
case NDPChassisBayStack200EthernetHub:
|
||||||
|
s = "BayStack 200 Ethernet Hub"
|
||||||
|
case NDPChassisBayStack25010100EthernetHub:
|
||||||
|
s = "BayStack 250 10/100 Ethernet Hub"
|
||||||
|
case NDPChassisBayStack450101001000Switches:
|
||||||
|
s = "BayStack 450 10/100/1000 Switches"
|
||||||
|
case NDPChassisBayStack41010100Switches:
|
||||||
|
s = "BayStack 410 10/100 Switches"
|
||||||
|
case NDPChassisPassport1200L3Switch:
|
||||||
|
s = "Passport 1200 L3 Switch"
|
||||||
|
case NDPChassisPassport1250L3Switch:
|
||||||
|
s = "Passport 1250 L3 Switch"
|
||||||
|
case NDPChassisPassport1100L3Switch:
|
||||||
|
s = "Passport 1100 L3 Switch"
|
||||||
|
case NDPChassisPassport1150L3Switch:
|
||||||
|
s = "Passport 1150 L3 Switch"
|
||||||
|
case NDPChassisPassport1050L3Switch:
|
||||||
|
s = "Passport 1050 L3 Switch"
|
||||||
|
case NDPChassisPassport1051L3Switch:
|
||||||
|
s = "Passport 1051 L3 Switch"
|
||||||
|
case NDPChassisPassport8610L3Switch:
|
||||||
|
s = "Passport 8610 L3 Switch"
|
||||||
|
case NDPChassisPassport8606L3Switch:
|
||||||
|
s = "Passport 8606 L3 Switch"
|
||||||
|
case NDPChassisPassport8010:
|
||||||
|
s = "Passport 8010"
|
||||||
|
case NDPChassisPassport8006:
|
||||||
|
s = "Passport 8006"
|
||||||
|
case NDPChassisBayStack670wirelessaccesspoint:
|
||||||
|
s = "BayStack 670 wireless access point"
|
||||||
|
case NDPChassisPassport740:
|
||||||
|
s = "Passport 740"
|
||||||
|
case NDPChassisPassport750:
|
||||||
|
s = "Passport 750"
|
||||||
|
case NDPChassisPassport790:
|
||||||
|
s = "Passport 790"
|
||||||
|
case NDPChassisBusinessPolicySwitch200010100Switches:
|
||||||
|
s = "Business Policy Switch 2000 10/100 Switches"
|
||||||
|
case NDPChassisPassport8110L2Switch:
|
||||||
|
s = "Passport 8110 L2 Switch"
|
||||||
|
case NDPChassisPassport8106L2Switch:
|
||||||
|
s = "Passport 8106 L2 Switch"
|
||||||
|
case NDPChassisBayStack3580GigSwitch:
|
||||||
|
s = "BayStack 3580 Gig Switch"
|
||||||
|
case NDPChassisBayStack10PowerSupplyUnit:
|
||||||
|
s = "BayStack 10 Power Supply Unit"
|
||||||
|
case NDPChassisBayStack42010100Switch:
|
||||||
|
s = "BayStack 420 10/100 Switch"
|
||||||
|
case NDPChassisOPTeraMetro1200EthernetServiceModule:
|
||||||
|
s = "OPTera Metro 1200 Ethernet Service Module"
|
||||||
|
case NDPChassisOPTera8010co:
|
||||||
|
s = "OPTera 8010co"
|
||||||
|
case NDPChassisOPTera8610coL3Switch:
|
||||||
|
s = "OPTera 8610co L3 switch"
|
||||||
|
case NDPChassisOPTera8110coL2Switch:
|
||||||
|
s = "OPTera 8110co L2 switch"
|
||||||
|
case NDPChassisOPTera8003:
|
||||||
|
s = "OPTera 8003"
|
||||||
|
case NDPChassisOPTera8603L3Switch:
|
||||||
|
s = "OPTera 8603 L3 switch"
|
||||||
|
case NDPChassisOPTera8103L2Switch:
|
||||||
|
s = "OPTera 8103 L2 switch"
|
||||||
|
case NDPChassisBayStack380101001000Switch:
|
||||||
|
s = "BayStack 380 10/100/1000 Switch"
|
||||||
|
case NDPChassisEthernetSwitch47048T:
|
||||||
|
s = "Ethernet Switch 470-48T"
|
||||||
|
case NDPChassisOPTeraMetro1450EthernetServiceModule:
|
||||||
|
s = "OPTera Metro 1450 Ethernet Service Module"
|
||||||
|
case NDPChassisOPTeraMetro1400EthernetServiceModule:
|
||||||
|
s = "OPTera Metro 1400 Ethernet Service Module"
|
||||||
|
case NDPChassisAlteonSwitchFamily:
|
||||||
|
s = "Alteon Switch Family"
|
||||||
|
case NDPChassisEthernetSwitch46024TPWR:
|
||||||
|
s = "Ethernet Switch 460-24T-PWR"
|
||||||
|
case NDPChassisOPTeraMetro8010OPML2Switch:
|
||||||
|
s = "OPTera Metro 8010 OPM L2 Switch"
|
||||||
|
case NDPChassisOPTeraMetro8010coOPML2Switch:
|
||||||
|
s = "OPTera Metro 8010co OPM L2 Switch"
|
||||||
|
case NDPChassisOPTeraMetro8006OPML2Switch:
|
||||||
|
s = "OPTera Metro 8006 OPM L2 Switch"
|
||||||
|
case NDPChassisOPTeraMetro8003OPML2Switch:
|
||||||
|
s = "OPTera Metro 8003 OPM L2 Switch"
|
||||||
|
case NDPChassisAlteon180e:
|
||||||
|
s = "Alteon 180e"
|
||||||
|
case NDPChassisAlteonAD3:
|
||||||
|
s = "Alteon AD3"
|
||||||
|
case NDPChassisAlteon184:
|
||||||
|
s = "Alteon 184"
|
||||||
|
case NDPChassisAlteonAD4:
|
||||||
|
s = "Alteon AD4"
|
||||||
|
case NDPChassisPassport1424L3Switch:
|
||||||
|
s = "Passport 1424 L3 switch"
|
||||||
|
case NDPChassisPassport1648L3Switch:
|
||||||
|
s = "Passport 1648 L3 switch"
|
||||||
|
case NDPChassisPassport1612L3Switch:
|
||||||
|
s = "Passport 1612 L3 switch"
|
||||||
|
case NDPChassisPassport1624L3Switch:
|
||||||
|
s = "Passport 1624 L3 switch"
|
||||||
|
case NDPChassisBayStack38024FFiber1000Switch:
|
||||||
|
s = "BayStack 380-24F Fiber 1000 Switch"
|
||||||
|
case NDPChassisEthernetRoutingSwitch551024T:
|
||||||
|
s = "Ethernet Routing Switch 5510-24T"
|
||||||
|
case NDPChassisEthernetRoutingSwitch551048T:
|
||||||
|
s = "Ethernet Routing Switch 5510-48T"
|
||||||
|
case NDPChassisEthernetSwitch47024T:
|
||||||
|
s = "Ethernet Switch 470-24T"
|
||||||
|
case NDPChassisNortelNetworksWirelessLANAccessPoint2220:
|
||||||
|
s = "Nortel Networks Wireless LAN Access Point 2220"
|
||||||
|
case NDPChassisPassportRBS2402L3Switch:
|
||||||
|
s = "Passport RBS 2402 L3 switch"
|
||||||
|
case NDPChassisAlteonApplicationSwitch2424:
|
||||||
|
s = "Alteon Application Switch 2424"
|
||||||
|
case NDPChassisAlteonApplicationSwitch2224:
|
||||||
|
s = "Alteon Application Switch 2224"
|
||||||
|
case NDPChassisAlteonApplicationSwitch2208:
|
||||||
|
s = "Alteon Application Switch 2208"
|
||||||
|
case NDPChassisAlteonApplicationSwitch2216:
|
||||||
|
s = "Alteon Application Switch 2216"
|
||||||
|
case NDPChassisAlteonApplicationSwitch3408:
|
||||||
|
s = "Alteon Application Switch 3408"
|
||||||
|
case NDPChassisAlteonApplicationSwitch3416:
|
||||||
|
s = "Alteon Application Switch 3416"
|
||||||
|
case NDPChassisNortelNetworksWirelessLANSecuritySwitch2250:
|
||||||
|
s = "Nortel Networks Wireless LAN SecuritySwitch 2250"
|
||||||
|
case NDPChassisEthernetSwitch42548T:
|
||||||
|
s = "Ethernet Switch 425-48T"
|
||||||
|
case NDPChassisEthernetSwitch42524T:
|
||||||
|
s = "Ethernet Switch 425-24T"
|
||||||
|
case NDPChassisNortelNetworksWirelessLANAccessPoint2221:
|
||||||
|
s = "Nortel Networks Wireless LAN Access Point 2221"
|
||||||
|
case NDPChassisNortelMetroEthernetServiceUnit24TSPFswitch:
|
||||||
|
s = "Nortel Metro Ethernet Service Unit 24-T SPF switch"
|
||||||
|
case NDPChassisNortelMetroEthernetServiceUnit24TLXDCswitch:
|
||||||
|
s = " Nortel Metro Ethernet Service Unit 24-T LX DC switch"
|
||||||
|
case NDPChassisPassport830010slotchassis:
|
||||||
|
s = "Passport 8300 10-slot chassis"
|
||||||
|
case NDPChassisPassport83006slotchassis:
|
||||||
|
s = "Passport 8300 6-slot chassis"
|
||||||
|
case NDPChassisEthernetRoutingSwitch552024TPWR:
|
||||||
|
s = "Ethernet Routing Switch 5520-24T-PWR"
|
||||||
|
case NDPChassisEthernetRoutingSwitch552048TPWR:
|
||||||
|
s = "Ethernet Routing Switch 5520-48T-PWR"
|
||||||
|
case NDPChassisNortelNetworksVPNGateway3050:
|
||||||
|
s = "Nortel Networks VPN Gateway 3050"
|
||||||
|
case NDPChassisAlteonSSL31010100:
|
||||||
|
s = "Alteon SSL 310 10/100"
|
||||||
|
case NDPChassisAlteonSSL31010100Fiber:
|
||||||
|
s = "Alteon SSL 310 10/100 Fiber"
|
||||||
|
case NDPChassisAlteonSSL31010100FIPS:
|
||||||
|
s = "Alteon SSL 310 10/100 FIPS"
|
||||||
|
case NDPChassisAlteonSSL410101001000:
|
||||||
|
s = "Alteon SSL 410 10/100/1000"
|
||||||
|
case NDPChassisAlteonSSL410101001000Fiber:
|
||||||
|
s = "Alteon SSL 410 10/100/1000 Fiber"
|
||||||
|
case NDPChassisAlteonApplicationSwitch2424SSL:
|
||||||
|
s = "Alteon Application Switch 2424-SSL"
|
||||||
|
case NDPChassisEthernetSwitch32524T:
|
||||||
|
s = "Ethernet Switch 325-24T"
|
||||||
|
case NDPChassisEthernetSwitch32524G:
|
||||||
|
s = "Ethernet Switch 325-24G"
|
||||||
|
case NDPChassisNortelNetworksWirelessLANAccessPoint2225:
|
||||||
|
s = "Nortel Networks Wireless LAN Access Point 2225"
|
||||||
|
case NDPChassisNortelNetworksWirelessLANSecuritySwitch2270:
|
||||||
|
s = "Nortel Networks Wireless LAN SecuritySwitch 2270"
|
||||||
|
case NDPChassis24portEthernetSwitch47024TPWR:
|
||||||
|
s = "24-port Ethernet Switch 470-24T-PWR"
|
||||||
|
case NDPChassis48portEthernetSwitch47048TPWR:
|
||||||
|
s = "48-port Ethernet Switch 470-48T-PWR"
|
||||||
|
case NDPChassisEthernetRoutingSwitch553024TFD:
|
||||||
|
s = "Ethernet Routing Switch 5530-24TFD"
|
||||||
|
case NDPChassisEthernetSwitch351024T:
|
||||||
|
s = "Ethernet Switch 3510-24T"
|
||||||
|
case NDPChassisNortelMetroEthernetServiceUnit12GACL3Switch:
|
||||||
|
s = "Nortel Metro Ethernet Service Unit 12G AC L3 switch"
|
||||||
|
case NDPChassisNortelMetroEthernetServiceUnit12GDCL3Switch:
|
||||||
|
s = "Nortel Metro Ethernet Service Unit 12G DC L3 switch"
|
||||||
|
case NDPChassisNortelSecureAccessSwitch:
|
||||||
|
s = "Nortel Secure Access Switch"
|
||||||
|
case NDPChassisNortelNetworksVPNGateway3070:
|
||||||
|
s = "Nortel Networks VPN Gateway 3070"
|
||||||
|
case NDPChassisOPTeraMetro3500:
|
||||||
|
s = "OPTera Metro 3500"
|
||||||
|
case NDPChassisSMBBES101024T:
|
||||||
|
s = "SMB BES 1010 24T"
|
||||||
|
case NDPChassisSMBBES101048T:
|
||||||
|
s = "SMB BES 1010 48T"
|
||||||
|
case NDPChassisSMBBES102024TPWR:
|
||||||
|
s = "SMB BES 1020 24T PWR"
|
||||||
|
case NDPChassisSMBBES102048TPWR:
|
||||||
|
s = "SMB BES 1020 48T PWR"
|
||||||
|
case NDPChassisSMBBES201024T:
|
||||||
|
s = "SMB BES 2010 24T"
|
||||||
|
case NDPChassisSMBBES201048T:
|
||||||
|
s = "SMB BES 2010 48T"
|
||||||
|
case NDPChassisSMBBES202024TPWR:
|
||||||
|
s = "SMB BES 2020 24T PWR"
|
||||||
|
case NDPChassisSMBBES202048TPWR:
|
||||||
|
s = "SMB BES 2020 48T PWR"
|
||||||
|
case NDPChassisSMBBES11024T:
|
||||||
|
s = "SMB BES 110 24T"
|
||||||
|
case NDPChassisSMBBES11048T:
|
||||||
|
s = "SMB BES 110 48T"
|
||||||
|
case NDPChassisSMBBES12024TPWR:
|
||||||
|
s = "SMB BES 120 24T PWR"
|
||||||
|
case NDPChassisSMBBES12048TPWR:
|
||||||
|
s = "SMB BES 120 48T PWR"
|
||||||
|
case NDPChassisSMBBES21024T:
|
||||||
|
s = "SMB BES 210 24T"
|
||||||
|
case NDPChassisSMBBES21048T:
|
||||||
|
s = "SMB BES 210 48T"
|
||||||
|
case NDPChassisSMBBES22024TPWR:
|
||||||
|
s = "SMB BES 220 24T PWR"
|
||||||
|
case NDPChassisSMBBES22048TPWR:
|
||||||
|
s = "SMB BES 220 48T PWR"
|
||||||
|
case NDPChassisOME6500:
|
||||||
|
s = "OME 6500"
|
||||||
|
case NDPChassisEthernetRoutingSwitch4548GT:
|
||||||
|
s = "Ethernet Routing Switch 4548GT"
|
||||||
|
case NDPChassisEthernetRoutingSwitch4548GTPWR:
|
||||||
|
s = "Ethernet Routing Switch 4548GT-PWR"
|
||||||
|
case NDPChassisEthernetRoutingSwitch4550T:
|
||||||
|
s = "Ethernet Routing Switch 4550T"
|
||||||
|
case NDPChassisEthernetRoutingSwitch4550TPWR:
|
||||||
|
s = "Ethernet Routing Switch 4550T-PWR"
|
||||||
|
case NDPChassisEthernetRoutingSwitch4526FX:
|
||||||
|
s = "Ethernet Routing Switch 4526FX"
|
||||||
|
case NDPChassisEthernetRoutingSwitch250026T:
|
||||||
|
s = "Ethernet Routing Switch 2500-26T"
|
||||||
|
case NDPChassisEthernetRoutingSwitch250026TPWR:
|
||||||
|
s = "Ethernet Routing Switch 2500-26T-PWR"
|
||||||
|
case NDPChassisEthernetRoutingSwitch250050T:
|
||||||
|
s = "Ethernet Routing Switch 2500-50T"
|
||||||
|
case NDPChassisEthernetRoutingSwitch250050TPWR:
|
||||||
|
s = "Ethernet Routing Switch 2500-50T-PWR"
|
||||||
|
default:
|
||||||
|
s = "Unknown"
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t NDPBackplaneType) String() (s string) {
|
||||||
|
switch t {
|
||||||
|
case NDPBackplaneOther:
|
||||||
|
s = "Other"
|
||||||
|
case NDPBackplaneEthernet:
|
||||||
|
s = "Ethernet"
|
||||||
|
case NDPBackplaneEthernetTokenring:
|
||||||
|
s = "Ethernet and Tokenring"
|
||||||
|
case NDPBackplaneEthernetFDDI:
|
||||||
|
s = "Ethernet and FDDI"
|
||||||
|
case NDPBackplaneEthernetTokenringFDDI:
|
||||||
|
s = "Ethernet, Tokenring and FDDI"
|
||||||
|
case NDPBackplaneEthernetTokenringRedundantPower:
|
||||||
|
s = "Ethernet and Tokenring with redundant power"
|
||||||
|
case NDPBackplaneEthernetTokenringFDDIRedundantPower:
|
||||||
|
s = "Ethernet, Tokenring, FDDI with redundant power"
|
||||||
|
case NDPBackplaneTokenRing:
|
||||||
|
s = "Token Ring"
|
||||||
|
case NDPBackplaneEthernetTokenringFastEthernet:
|
||||||
|
s = "Ethernet, Tokenring and Fast Ethernet"
|
||||||
|
case NDPBackplaneEthernetFastEthernet:
|
||||||
|
s = "Ethernet and Fast Ethernet"
|
||||||
|
case NDPBackplaneEthernetTokenringFastEthernetRedundantPower:
|
||||||
|
s = "Ethernet, Tokenring, Fast Ethernet with redundant power"
|
||||||
|
case NDPBackplaneEthernetFastEthernetGigabitEthernet:
|
||||||
|
s = "Ethernet, Fast Ethernet and Gigabit Ethernet"
|
||||||
|
default:
|
||||||
|
s = "Unknown"
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t NDPState) String() (s string) {
|
||||||
|
switch t {
|
||||||
|
case NDPStateTopology:
|
||||||
|
s = "Topology Change"
|
||||||
|
case NDPStateHeartbeat:
|
||||||
|
s = "Heartbeat"
|
||||||
|
case NDPStateNew:
|
||||||
|
s = "New"
|
||||||
|
default:
|
||||||
|
s = "Unknown"
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
|
@ -0,0 +1,416 @@
|
||||||
|
// 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"
|
||||||
|
)
|
||||||
|
|
||||||
|
//******************************************************************************
|
||||||
|
//
|
||||||
|
// Network Time Protocol (NTP) Decoding Layer
|
||||||
|
// ------------------------------------------
|
||||||
|
// This file provides a GoPacket decoding layer for NTP.
|
||||||
|
//
|
||||||
|
//******************************************************************************
|
||||||
|
//
|
||||||
|
// About The Network Time Protocol (NTP)
|
||||||
|
// -------------------------------------
|
||||||
|
// NTP is a protocol that enables computers on the internet to set their
|
||||||
|
// clocks to the correct time (or to a time that is acceptably close to the
|
||||||
|
// correct time). NTP runs on top of UDP.
|
||||||
|
//
|
||||||
|
// There have been a series of versions of the NTP protocol. The latest
|
||||||
|
// version is V4 and is specified in RFC 5905:
|
||||||
|
// http://www.ietf.org/rfc/rfc5905.txt
|
||||||
|
//
|
||||||
|
//******************************************************************************
|
||||||
|
//
|
||||||
|
// References
|
||||||
|
// ----------
|
||||||
|
//
|
||||||
|
// Wikipedia's NTP entry:
|
||||||
|
// https://en.wikipedia.org/wiki/Network_Time_Protocol
|
||||||
|
// This is the best place to get an overview of NTP.
|
||||||
|
//
|
||||||
|
// Network Time Protocol Home Website:
|
||||||
|
// http://www.ntp.org/
|
||||||
|
// This appears to be the official website of NTP.
|
||||||
|
//
|
||||||
|
// List of current NTP Protocol RFCs:
|
||||||
|
// http://www.ntp.org/rfc.html
|
||||||
|
//
|
||||||
|
// RFC 958: "Network Time Protocol (NTP)" (1985)
|
||||||
|
// https://tools.ietf.org/html/rfc958
|
||||||
|
// This is the original NTP specification.
|
||||||
|
//
|
||||||
|
// RFC 1305: "Network Time Protocol (Version 3) Specification, Implementation and Analysis" (1992)
|
||||||
|
// https://tools.ietf.org/html/rfc1305
|
||||||
|
// The protocol was updated in 1992 yielding NTP V3.
|
||||||
|
//
|
||||||
|
// RFC 5905: "Network Time Protocol Version 4: Protocol and Algorithms Specification" (2010)
|
||||||
|
// https://www.ietf.org/rfc/rfc5905.txt
|
||||||
|
// The protocol was updated in 2010 yielding NTP V4.
|
||||||
|
// V4 is backwards compatible with all previous versions of NTP.
|
||||||
|
//
|
||||||
|
// RFC 5906: "Network Time Protocol Version 4: Autokey Specification"
|
||||||
|
// https://tools.ietf.org/html/rfc5906
|
||||||
|
// This document addresses the security of the NTP protocol
|
||||||
|
// and is probably not relevant to this package.
|
||||||
|
//
|
||||||
|
// RFC 5907: "Definitions of Managed Objects for Network Time Protocol Version 4 (NTPv4)"
|
||||||
|
// https://tools.ietf.org/html/rfc5907
|
||||||
|
// This document addresses the management of NTP servers and
|
||||||
|
// is probably not relevant to this package.
|
||||||
|
//
|
||||||
|
// RFC 5908: "Network Time Protocol (NTP) Server Option for DHCPv6"
|
||||||
|
// https://tools.ietf.org/html/rfc5908
|
||||||
|
// This document addresses the use of NTP in DHCPv6 and is
|
||||||
|
// probably not relevant to this package.
|
||||||
|
//
|
||||||
|
// "Let's make a NTP Client in C"
|
||||||
|
// https://lettier.github.io/posts/2016-04-26-lets-make-a-ntp-client-in-c.html
|
||||||
|
// This web page contains useful information about the details of NTP,
|
||||||
|
// including an NTP record struture in C, and C code.
|
||||||
|
//
|
||||||
|
// "NTP Packet Header (NTP Reference Implementation) (Computer Network Time Synchronization)"
|
||||||
|
// http://what-when-how.com/computer-network-time-synchronization/
|
||||||
|
// ntp-packet-header-ntp-reference-implementation-computer-network-time-synchronization/
|
||||||
|
// This web page contains useful information on the details of NTP.
|
||||||
|
//
|
||||||
|
// "Technical information - NTP Data Packet"
|
||||||
|
// https://www.meinbergglobal.com/english/info/ntp-packet.htm
|
||||||
|
// This page has a helpful diagram of an NTP V4 packet.
|
||||||
|
//
|
||||||
|
//******************************************************************************
|
||||||
|
//
|
||||||
|
// Obsolete References
|
||||||
|
// -------------------
|
||||||
|
//
|
||||||
|
// RFC 1119: "RFC-1119 "Network Time Protocol (Version 2) Specification and Implementation" (1989)
|
||||||
|
// https://tools.ietf.org/html/rfc1119
|
||||||
|
// Version 2 was drafted in 1989.
|
||||||
|
// It is unclear whether V2 was ever implememented or whether the
|
||||||
|
// ideas ended up in V3 (which was implemented in 1992).
|
||||||
|
//
|
||||||
|
// RFC 1361: "Simple Network Time Protocol (SNTP)"
|
||||||
|
// https://tools.ietf.org/html/rfc1361
|
||||||
|
// This document is obsoleted by RFC 1769 and is included only for completeness.
|
||||||
|
//
|
||||||
|
// RFC 1769: "Simple Network Time Protocol (SNTP)"
|
||||||
|
// https://tools.ietf.org/html/rfc1769
|
||||||
|
// This document is obsoleted by RFC 2030 and RFC 4330 and is included only for completeness.
|
||||||
|
//
|
||||||
|
// RFC 2030: "Simple Network Time Protocol (SNTP) Version 4 for IPv4, IPv6 and OSI"
|
||||||
|
// https://tools.ietf.org/html/rfc2030
|
||||||
|
// This document is obsoleted by RFC 4330 and is included only for completeness.
|
||||||
|
//
|
||||||
|
// RFC 4330: "Simple Network Time Protocol (SNTP) Version 4 for IPv4, IPv6 and OSI"
|
||||||
|
// https://tools.ietf.org/html/rfc4330
|
||||||
|
// This document is obsoleted by RFC 5905 and is included only for completeness.
|
||||||
|
//
|
||||||
|
//******************************************************************************
|
||||||
|
//
|
||||||
|
// Endian And Bit Numbering Issues
|
||||||
|
// -------------------------------
|
||||||
|
//
|
||||||
|
// Endian and bit numbering issues can be confusing. Here is some
|
||||||
|
// clarification:
|
||||||
|
//
|
||||||
|
// ENDIAN: Values are sent big endian.
|
||||||
|
// https://en.wikipedia.org/wiki/Endianness
|
||||||
|
//
|
||||||
|
// BIT NUMBERING: Bits are numbered 0 upwards from the most significant
|
||||||
|
// bit to the least significant bit. This means that if there is a 32-bit
|
||||||
|
// value, the most significant bit is called bit 0 and the least
|
||||||
|
// significant bit is called bit 31.
|
||||||
|
//
|
||||||
|
// See RFC 791 Appendix B for more discussion.
|
||||||
|
//
|
||||||
|
//******************************************************************************
|
||||||
|
//
|
||||||
|
// NTP V3 and V4 Packet Format
|
||||||
|
// ---------------------------
|
||||||
|
// NTP packets are UDP packets whose payload contains an NTP record.
|
||||||
|
//
|
||||||
|
// The NTP RFC defines the format of the NTP record.
|
||||||
|
//
|
||||||
|
// There have been four versions of the protocol:
|
||||||
|
//
|
||||||
|
// V1 in 1985
|
||||||
|
// V2 in 1989
|
||||||
|
// V3 in 1992
|
||||||
|
// V4 in 2010
|
||||||
|
//
|
||||||
|
// It is clear that V1 and V2 are obsolete, and there is no need to
|
||||||
|
// cater for these formats.
|
||||||
|
//
|
||||||
|
// V3 and V4 essentially use the same format, with V4 adding some optional
|
||||||
|
// fields on the end. So this package supports the V3 and V4 formats.
|
||||||
|
//
|
||||||
|
// The current version of NTP (NTP V4)'s RFC (V4 - RFC 5905) contains
|
||||||
|
// the following diagram for the NTP record 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
|
||||||
|
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
|
// |LI | VN |Mode | Stratum | Poll | Precision |
|
||||||
|
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
|
// | Root Delay |
|
||||||
|
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
|
// | Root Dispersion |
|
||||||
|
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
|
// | Reference ID |
|
||||||
|
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
|
// | |
|
||||||
|
// + Reference Timestamp (64) +
|
||||||
|
// | |
|
||||||
|
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
|
// | |
|
||||||
|
// + Origin Timestamp (64) +
|
||||||
|
// | |
|
||||||
|
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
|
// | |
|
||||||
|
// + Receive Timestamp (64) +
|
||||||
|
// | |
|
||||||
|
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
|
// | |
|
||||||
|
// + Transmit Timestamp (64) +
|
||||||
|
// | |
|
||||||
|
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
|
// | |
|
||||||
|
// . .
|
||||||
|
// . Extension Field 1 (variable) .
|
||||||
|
// . .
|
||||||
|
// | |
|
||||||
|
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
|
// | |
|
||||||
|
// . .
|
||||||
|
// . Extension Field 2 (variable) .
|
||||||
|
// . .
|
||||||
|
// | |
|
||||||
|
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
|
// | Key Identifier |
|
||||||
|
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
|
// | |
|
||||||
|
// | dgst (128) |
|
||||||
|
// | |
|
||||||
|
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
|
// From http://www.ietf.org/rfc/rfc5905.txt
|
||||||
|
//
|
||||||
|
// The fields "Extension Field 1 (variable)" and later are optional fields,
|
||||||
|
// and so we can set a minimum NTP record size of 48 bytes.
|
||||||
|
//
|
||||||
|
const ntpMinimumRecordSizeInBytes int = 48
|
||||||
|
|
||||||
|
//******************************************************************************
|
||||||
|
|
||||||
|
// NTP Type
|
||||||
|
// --------
|
||||||
|
// Type NTP implements the DecodingLayer interface. Each NTP object
|
||||||
|
// represents in a structured form the NTP record present as the UDP
|
||||||
|
// payload in an NTP UDP packet.
|
||||||
|
//
|
||||||
|
|
||||||
|
type NTPLeapIndicator uint8
|
||||||
|
type NTPVersion uint8
|
||||||
|
type NTPMode uint8
|
||||||
|
type NTPStratum uint8
|
||||||
|
type NTPLog2Seconds int8
|
||||||
|
type NTPFixed16Seconds uint32
|
||||||
|
type NTPReferenceID uint32
|
||||||
|
type NTPTimestamp uint64
|
||||||
|
|
||||||
|
type NTP struct {
|
||||||
|
BaseLayer // Stores the packet bytes and payload bytes.
|
||||||
|
|
||||||
|
LeapIndicator NTPLeapIndicator // [0,3]. Indicates whether leap second(s) is to be added.
|
||||||
|
Version NTPVersion // [0,7]. Version of the NTP protocol.
|
||||||
|
Mode NTPMode // [0,7]. Mode.
|
||||||
|
Stratum NTPStratum // [0,255]. Stratum of time server in the server tree.
|
||||||
|
Poll NTPLog2Seconds // [-128,127]. The maximum interval between successive messages, in log2 seconds.
|
||||||
|
Precision NTPLog2Seconds // [-128,127]. The precision of the system clock, in log2 seconds.
|
||||||
|
RootDelay NTPFixed16Seconds // [0,2^32-1]. Total round trip delay to the reference clock in seconds times 2^16.
|
||||||
|
RootDispersion NTPFixed16Seconds // [0,2^32-1]. Total dispersion to the reference clock, in seconds times 2^16.
|
||||||
|
ReferenceID NTPReferenceID // ID code of reference clock [0,2^32-1].
|
||||||
|
ReferenceTimestamp NTPTimestamp // Most recent timestamp from the reference clock.
|
||||||
|
OriginTimestamp NTPTimestamp // Local time when request was sent from local host.
|
||||||
|
ReceiveTimestamp NTPTimestamp // Local time (on server) that request arrived at server host.
|
||||||
|
TransmitTimestamp NTPTimestamp // Local time (on server) that request departed server host.
|
||||||
|
|
||||||
|
// FIX: This package should analyse the extension fields and represent the extension fields too.
|
||||||
|
ExtensionBytes []byte // Just put extensions in a byte slice.
|
||||||
|
}
|
||||||
|
|
||||||
|
//******************************************************************************
|
||||||
|
|
||||||
|
// LayerType returns the layer type of the NTP object, which is LayerTypeNTP.
|
||||||
|
func (d *NTP) LayerType() gopacket.LayerType {
|
||||||
|
return LayerTypeNTP
|
||||||
|
}
|
||||||
|
|
||||||
|
//******************************************************************************
|
||||||
|
|
||||||
|
// decodeNTP analyses a byte slice and attempts to decode it as an NTP
|
||||||
|
// record of a UDP 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 NTP layer.
|
||||||
|
func decodeNTP(data []byte, p gopacket.PacketBuilder) error {
|
||||||
|
|
||||||
|
// Attempt to decode the byte slice.
|
||||||
|
d := &NTP{}
|
||||||
|
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 an NTP
|
||||||
|
// record of a UDP packet.
|
||||||
|
//
|
||||||
|
// Upon succeeds, it loads the NTP object with information about the packet
|
||||||
|
// and returns nil.
|
||||||
|
// Upon failure, it returns an error (non nil).
|
||||||
|
func (d *NTP) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
|
||||||
|
|
||||||
|
// If the data block is too short to be a NTP record, then return an error.
|
||||||
|
if len(data) < ntpMinimumRecordSizeInBytes {
|
||||||
|
df.SetTruncated()
|
||||||
|
return errors.New("NTP packet too short")
|
||||||
|
}
|
||||||
|
|
||||||
|
// RFC 5905 does not appear to define a maximum NTP record length.
|
||||||
|
// The protocol allows "extension fields" to be included in the record,
|
||||||
|
// and states about these fields:"
|
||||||
|
//
|
||||||
|
// "While the minimum field length containing required fields is
|
||||||
|
// four words (16 octets), a maximum field length remains to be
|
||||||
|
// established."
|
||||||
|
//
|
||||||
|
// For this reason, the packet length is not checked here for being too long.
|
||||||
|
|
||||||
|
// NTP 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 NTP 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.
|
||||||
|
f := data[0]
|
||||||
|
d.LeapIndicator = NTPLeapIndicator((f & 0xC0) >> 6)
|
||||||
|
d.Version = NTPVersion((f & 0x38) >> 3)
|
||||||
|
d.Mode = NTPMode(f & 0x07)
|
||||||
|
d.Stratum = NTPStratum(data[1])
|
||||||
|
d.Poll = NTPLog2Seconds(data[2])
|
||||||
|
d.Precision = NTPLog2Seconds(data[3])
|
||||||
|
|
||||||
|
// The remaining fields can just be copied in big endian order.
|
||||||
|
d.RootDelay = NTPFixed16Seconds(binary.BigEndian.Uint32(data[4:8]))
|
||||||
|
d.RootDispersion = NTPFixed16Seconds(binary.BigEndian.Uint32(data[8:12]))
|
||||||
|
d.ReferenceID = NTPReferenceID(binary.BigEndian.Uint32(data[12:16]))
|
||||||
|
d.ReferenceTimestamp = NTPTimestamp(binary.BigEndian.Uint64(data[16:24]))
|
||||||
|
d.OriginTimestamp = NTPTimestamp(binary.BigEndian.Uint64(data[24:32]))
|
||||||
|
d.ReceiveTimestamp = NTPTimestamp(binary.BigEndian.Uint64(data[32:40]))
|
||||||
|
d.TransmitTimestamp = NTPTimestamp(binary.BigEndian.Uint64(data[40:48]))
|
||||||
|
|
||||||
|
// This layer does not attempt to analyse the extension bytes.
|
||||||
|
// But if there are any, we'd like the user to know. So we just
|
||||||
|
// place them all in an ExtensionBytes field.
|
||||||
|
d.ExtensionBytes = data[48:]
|
||||||
|
|
||||||
|
// Return no error.
|
||||||
|
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 *NTP) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
|
||||||
|
data, err := b.PrependBytes(ntpMinimumRecordSizeInBytes)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pack the first few fields into the first 32 bits.
|
||||||
|
h := uint8(0)
|
||||||
|
h |= (uint8(d.LeapIndicator) << 6) & 0xC0
|
||||||
|
h |= (uint8(d.Version) << 3) & 0x38
|
||||||
|
h |= (uint8(d.Mode)) & 0x07
|
||||||
|
data[0] = byte(h)
|
||||||
|
data[1] = byte(d.Stratum)
|
||||||
|
data[2] = byte(d.Poll)
|
||||||
|
data[3] = byte(d.Precision)
|
||||||
|
|
||||||
|
// The remaining fields can just be copied in big endian order.
|
||||||
|
binary.BigEndian.PutUint32(data[4:8], uint32(d.RootDelay))
|
||||||
|
binary.BigEndian.PutUint32(data[8:12], uint32(d.RootDispersion))
|
||||||
|
binary.BigEndian.PutUint32(data[12:16], uint32(d.ReferenceID))
|
||||||
|
binary.BigEndian.PutUint64(data[16:24], uint64(d.ReferenceTimestamp))
|
||||||
|
binary.BigEndian.PutUint64(data[24:32], uint64(d.OriginTimestamp))
|
||||||
|
binary.BigEndian.PutUint64(data[32:40], uint64(d.ReceiveTimestamp))
|
||||||
|
binary.BigEndian.PutUint64(data[40:48], uint64(d.TransmitTimestamp))
|
||||||
|
|
||||||
|
ex, err := b.AppendBytes(len(d.ExtensionBytes))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
copy(ex, d.ExtensionBytes)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
//******************************************************************************
|
||||||
|
|
||||||
|
// CanDecode returns a set of layers that NTP objects can decode.
|
||||||
|
// As NTP objects can only decide the NTP layer, we can return just that layer.
|
||||||
|
// Apparently a single layer type implements LayerClass.
|
||||||
|
func (d *NTP) CanDecode() gopacket.LayerClass {
|
||||||
|
return LayerTypeNTP
|
||||||
|
}
|
||||||
|
|
||||||
|
//******************************************************************************
|
||||||
|
|
||||||
|
// NextLayerType specifies the next layer that GoPacket should attempt to
|
||||||
|
// analyse after this (NTP) layer. As NTP packets do not contain any payload
|
||||||
|
// bytes, there are no further layers to analyse.
|
||||||
|
func (d *NTP) NextLayerType() gopacket.LayerType {
|
||||||
|
return gopacket.LayerTypeZero
|
||||||
|
}
|
||||||
|
|
||||||
|
//******************************************************************************
|
||||||
|
|
||||||
|
// NTP packets do not carry any data payload, so the empty byte slice is retured.
|
||||||
|
// In Go, a nil slice is functionally identical to an empty slice, so we
|
||||||
|
// return nil to avoid a heap allocation.
|
||||||
|
func (d *NTP) Payload() []byte {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
//******************************************************************************
|
||||||
|
//* End Of NTP File *
|
||||||
|
//******************************************************************************
|
|
@ -0,0 +1,715 @@
|
||||||
|
// 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"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/google/gopacket"
|
||||||
|
)
|
||||||
|
|
||||||
|
// OSPFType denotes what kind of OSPF type it is
|
||||||
|
type OSPFType uint8
|
||||||
|
|
||||||
|
// Potential values for OSPF.Type.
|
||||||
|
const (
|
||||||
|
OSPFHello OSPFType = 1
|
||||||
|
OSPFDatabaseDescription OSPFType = 2
|
||||||
|
OSPFLinkStateRequest OSPFType = 3
|
||||||
|
OSPFLinkStateUpdate OSPFType = 4
|
||||||
|
OSPFLinkStateAcknowledgment OSPFType = 5
|
||||||
|
)
|
||||||
|
|
||||||
|
// LSA Function Codes for LSAheader.LSType
|
||||||
|
const (
|
||||||
|
RouterLSAtypeV2 = 0x1
|
||||||
|
RouterLSAtype = 0x2001
|
||||||
|
NetworkLSAtypeV2 = 0x2
|
||||||
|
NetworkLSAtype = 0x2002
|
||||||
|
SummaryLSANetworktypeV2 = 0x3
|
||||||
|
InterAreaPrefixLSAtype = 0x2003
|
||||||
|
SummaryLSAASBRtypeV2 = 0x4
|
||||||
|
InterAreaRouterLSAtype = 0x2004
|
||||||
|
ASExternalLSAtypeV2 = 0x5
|
||||||
|
ASExternalLSAtype = 0x4005
|
||||||
|
NSSALSAtype = 0x2007
|
||||||
|
NSSALSAtypeV2 = 0x7
|
||||||
|
LinkLSAtype = 0x0008
|
||||||
|
IntraAreaPrefixLSAtype = 0x2009
|
||||||
|
)
|
||||||
|
|
||||||
|
// String conversions for OSPFType
|
||||||
|
func (i OSPFType) String() string {
|
||||||
|
switch i {
|
||||||
|
case OSPFHello:
|
||||||
|
return "Hello"
|
||||||
|
case OSPFDatabaseDescription:
|
||||||
|
return "Database Description"
|
||||||
|
case OSPFLinkStateRequest:
|
||||||
|
return "Link State Request"
|
||||||
|
case OSPFLinkStateUpdate:
|
||||||
|
return "Link State Update"
|
||||||
|
case OSPFLinkStateAcknowledgment:
|
||||||
|
return "Link State Acknowledgment"
|
||||||
|
default:
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prefix extends IntraAreaPrefixLSA
|
||||||
|
type Prefix struct {
|
||||||
|
PrefixLength uint8
|
||||||
|
PrefixOptions uint8
|
||||||
|
Metric uint16
|
||||||
|
AddressPrefix []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
// IntraAreaPrefixLSA is the struct from RFC 5340 A.4.10.
|
||||||
|
type IntraAreaPrefixLSA struct {
|
||||||
|
NumOfPrefixes uint16
|
||||||
|
RefLSType uint16
|
||||||
|
RefLinkStateID uint32
|
||||||
|
RefAdvRouter uint32
|
||||||
|
Prefixes []Prefix
|
||||||
|
}
|
||||||
|
|
||||||
|
// LinkLSA is the struct from RFC 5340 A.4.9.
|
||||||
|
type LinkLSA struct {
|
||||||
|
RtrPriority uint8
|
||||||
|
Options uint32
|
||||||
|
LinkLocalAddress []byte
|
||||||
|
NumOfPrefixes uint32
|
||||||
|
Prefixes []Prefix
|
||||||
|
}
|
||||||
|
|
||||||
|
// ASExternalLSAV2 is the struct from RFC 2328 A.4.5.
|
||||||
|
type ASExternalLSAV2 struct {
|
||||||
|
NetworkMask uint32
|
||||||
|
ExternalBit uint8
|
||||||
|
Metric uint32
|
||||||
|
ForwardingAddress uint32
|
||||||
|
ExternalRouteTag uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
// ASExternalLSA is the struct from RFC 5340 A.4.7.
|
||||||
|
type ASExternalLSA struct {
|
||||||
|
Flags uint8
|
||||||
|
Metric uint32
|
||||||
|
PrefixLength uint8
|
||||||
|
PrefixOptions uint8
|
||||||
|
RefLSType uint16
|
||||||
|
AddressPrefix []byte
|
||||||
|
ForwardingAddress []byte
|
||||||
|
ExternalRouteTag uint32
|
||||||
|
RefLinkStateID uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
// InterAreaRouterLSA is the struct from RFC 5340 A.4.6.
|
||||||
|
type InterAreaRouterLSA struct {
|
||||||
|
Options uint32
|
||||||
|
Metric uint32
|
||||||
|
DestinationRouterID uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
// InterAreaPrefixLSA is the struct from RFC 5340 A.4.5.
|
||||||
|
type InterAreaPrefixLSA struct {
|
||||||
|
Metric uint32
|
||||||
|
PrefixLength uint8
|
||||||
|
PrefixOptions uint8
|
||||||
|
AddressPrefix []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
// NetworkLSA is the struct from RFC 5340 A.4.4.
|
||||||
|
type NetworkLSA struct {
|
||||||
|
Options uint32
|
||||||
|
AttachedRouter []uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
// NetworkLSAV2 is the struct from RFC 2328 A.4.3.
|
||||||
|
type NetworkLSAV2 struct {
|
||||||
|
NetworkMask uint32
|
||||||
|
AttachedRouter []uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
// RouterV2 extends RouterLSAV2
|
||||||
|
type RouterV2 struct {
|
||||||
|
Type uint8
|
||||||
|
LinkID uint32
|
||||||
|
LinkData uint32
|
||||||
|
Metric uint16
|
||||||
|
}
|
||||||
|
|
||||||
|
// RouterLSAV2 is the struct from RFC 2328 A.4.2.
|
||||||
|
type RouterLSAV2 struct {
|
||||||
|
Flags uint8
|
||||||
|
Links uint16
|
||||||
|
Routers []RouterV2
|
||||||
|
}
|
||||||
|
|
||||||
|
// Router extends RouterLSA
|
||||||
|
type Router struct {
|
||||||
|
Type uint8
|
||||||
|
Metric uint16
|
||||||
|
InterfaceID uint32
|
||||||
|
NeighborInterfaceID uint32
|
||||||
|
NeighborRouterID uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
// RouterLSA is the struct from RFC 5340 A.4.3.
|
||||||
|
type RouterLSA struct {
|
||||||
|
Flags uint8
|
||||||
|
Options uint32
|
||||||
|
Routers []Router
|
||||||
|
}
|
||||||
|
|
||||||
|
// LSAheader is the struct from RFC 5340 A.4.2 and RFC 2328 A.4.1.
|
||||||
|
type LSAheader struct {
|
||||||
|
LSAge uint16
|
||||||
|
LSType uint16
|
||||||
|
LinkStateID uint32
|
||||||
|
AdvRouter uint32
|
||||||
|
LSSeqNumber uint32
|
||||||
|
LSChecksum uint16
|
||||||
|
Length uint16
|
||||||
|
LSOptions uint8
|
||||||
|
}
|
||||||
|
|
||||||
|
// LSA links LSAheader with the structs from RFC 5340 A.4.
|
||||||
|
type LSA struct {
|
||||||
|
LSAheader
|
||||||
|
Content interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// LSUpdate is the struct from RFC 5340 A.3.5.
|
||||||
|
type LSUpdate struct {
|
||||||
|
NumOfLSAs uint32
|
||||||
|
LSAs []LSA
|
||||||
|
}
|
||||||
|
|
||||||
|
// LSReq is the struct from RFC 5340 A.3.4.
|
||||||
|
type LSReq struct {
|
||||||
|
LSType uint16
|
||||||
|
LSID uint32
|
||||||
|
AdvRouter uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
// DbDescPkg is the struct from RFC 5340 A.3.3.
|
||||||
|
type DbDescPkg struct {
|
||||||
|
Options uint32
|
||||||
|
InterfaceMTU uint16
|
||||||
|
Flags uint16
|
||||||
|
DDSeqNumber uint32
|
||||||
|
LSAinfo []LSAheader
|
||||||
|
}
|
||||||
|
|
||||||
|
// HelloPkg is the struct from RFC 5340 A.3.2.
|
||||||
|
type HelloPkg struct {
|
||||||
|
InterfaceID uint32
|
||||||
|
RtrPriority uint8
|
||||||
|
Options uint32
|
||||||
|
HelloInterval uint16
|
||||||
|
RouterDeadInterval uint32
|
||||||
|
DesignatedRouterID uint32
|
||||||
|
BackupDesignatedRouterID uint32
|
||||||
|
NeighborID []uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
// HelloPkgV2 extends the HelloPkg struct with OSPFv2 information
|
||||||
|
type HelloPkgV2 struct {
|
||||||
|
HelloPkg
|
||||||
|
NetworkMask uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
// OSPF is a basic OSPF packet header with common fields of Version 2 and Version 3.
|
||||||
|
type OSPF struct {
|
||||||
|
Version uint8
|
||||||
|
Type OSPFType
|
||||||
|
PacketLength uint16
|
||||||
|
RouterID uint32
|
||||||
|
AreaID uint32
|
||||||
|
Checksum uint16
|
||||||
|
Content interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
//OSPFv2 extend the OSPF head with version 2 specific fields
|
||||||
|
type OSPFv2 struct {
|
||||||
|
BaseLayer
|
||||||
|
OSPF
|
||||||
|
AuType uint16
|
||||||
|
Authentication uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
// OSPFv3 extend the OSPF head with version 3 specific fields
|
||||||
|
type OSPFv3 struct {
|
||||||
|
BaseLayer
|
||||||
|
OSPF
|
||||||
|
Instance uint8
|
||||||
|
Reserved uint8
|
||||||
|
}
|
||||||
|
|
||||||
|
// getLSAsv2 parses the LSA information from the packet for OSPFv2
|
||||||
|
func getLSAsv2(num uint32, data []byte) ([]LSA, error) {
|
||||||
|
var lsas []LSA
|
||||||
|
var i uint32 = 0
|
||||||
|
var offset uint32 = 0
|
||||||
|
for ; i < num; i++ {
|
||||||
|
lstype := uint16(data[offset+3])
|
||||||
|
lsalength := binary.BigEndian.Uint16(data[offset+18 : offset+20])
|
||||||
|
content, err := extractLSAInformation(lstype, lsalength, data[offset:])
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("Could not extract Link State type.")
|
||||||
|
}
|
||||||
|
lsa := LSA{
|
||||||
|
LSAheader: LSAheader{
|
||||||
|
LSAge: binary.BigEndian.Uint16(data[offset : offset+2]),
|
||||||
|
LSOptions: data[offset+2],
|
||||||
|
LSType: lstype,
|
||||||
|
LinkStateID: binary.BigEndian.Uint32(data[offset+4 : offset+8]),
|
||||||
|
AdvRouter: binary.BigEndian.Uint32(data[offset+8 : offset+12]),
|
||||||
|
LSSeqNumber: binary.BigEndian.Uint32(data[offset+12 : offset+16]),
|
||||||
|
LSChecksum: binary.BigEndian.Uint16(data[offset+16 : offset+18]),
|
||||||
|
Length: lsalength,
|
||||||
|
},
|
||||||
|
Content: content,
|
||||||
|
}
|
||||||
|
lsas = append(lsas, lsa)
|
||||||
|
offset += uint32(lsalength)
|
||||||
|
}
|
||||||
|
return lsas, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// extractLSAInformation extracts all the LSA information
|
||||||
|
func extractLSAInformation(lstype, lsalength uint16, data []byte) (interface{}, error) {
|
||||||
|
if lsalength < 20 {
|
||||||
|
return nil, fmt.Errorf("Link State header length %v too short, %v required", lsalength, 20)
|
||||||
|
}
|
||||||
|
if len(data) < int(lsalength) {
|
||||||
|
return nil, fmt.Errorf("Link State header length %v too short, %v required", len(data), lsalength)
|
||||||
|
}
|
||||||
|
var content interface{}
|
||||||
|
switch lstype {
|
||||||
|
case RouterLSAtypeV2:
|
||||||
|
var routers []RouterV2
|
||||||
|
var j uint32
|
||||||
|
for j = 24; j < uint32(lsalength); j += 12 {
|
||||||
|
if len(data) < int(j+12) {
|
||||||
|
return nil, errors.New("LSAtypeV2 too small")
|
||||||
|
}
|
||||||
|
router := RouterV2{
|
||||||
|
LinkID: binary.BigEndian.Uint32(data[j : j+4]),
|
||||||
|
LinkData: binary.BigEndian.Uint32(data[j+4 : j+8]),
|
||||||
|
Type: uint8(data[j+8]),
|
||||||
|
Metric: binary.BigEndian.Uint16(data[j+10 : j+12]),
|
||||||
|
}
|
||||||
|
routers = append(routers, router)
|
||||||
|
}
|
||||||
|
if len(data) < 24 {
|
||||||
|
return nil, errors.New("LSAtypeV2 too small")
|
||||||
|
}
|
||||||
|
links := binary.BigEndian.Uint16(data[22:24])
|
||||||
|
content = RouterLSAV2{
|
||||||
|
Flags: data[20],
|
||||||
|
Links: links,
|
||||||
|
Routers: routers,
|
||||||
|
}
|
||||||
|
case NSSALSAtypeV2:
|
||||||
|
fallthrough
|
||||||
|
case ASExternalLSAtypeV2:
|
||||||
|
content = ASExternalLSAV2{
|
||||||
|
NetworkMask: binary.BigEndian.Uint32(data[20:24]),
|
||||||
|
ExternalBit: data[24] & 0x80,
|
||||||
|
Metric: binary.BigEndian.Uint32(data[24:28]) & 0x00FFFFFF,
|
||||||
|
ForwardingAddress: binary.BigEndian.Uint32(data[28:32]),
|
||||||
|
ExternalRouteTag: binary.BigEndian.Uint32(data[32:36]),
|
||||||
|
}
|
||||||
|
case NetworkLSAtypeV2:
|
||||||
|
var routers []uint32
|
||||||
|
var j uint32
|
||||||
|
for j = 24; j < uint32(lsalength); j += 4 {
|
||||||
|
routers = append(routers, binary.BigEndian.Uint32(data[j:j+4]))
|
||||||
|
}
|
||||||
|
content = NetworkLSAV2{
|
||||||
|
NetworkMask: binary.BigEndian.Uint32(data[20:24]),
|
||||||
|
AttachedRouter: routers,
|
||||||
|
}
|
||||||
|
case RouterLSAtype:
|
||||||
|
var routers []Router
|
||||||
|
var j uint32
|
||||||
|
for j = 24; j < uint32(lsalength); j += 16 {
|
||||||
|
router := Router{
|
||||||
|
Type: uint8(data[j]),
|
||||||
|
Metric: binary.BigEndian.Uint16(data[j+2 : j+4]),
|
||||||
|
InterfaceID: binary.BigEndian.Uint32(data[j+4 : j+8]),
|
||||||
|
NeighborInterfaceID: binary.BigEndian.Uint32(data[j+8 : j+12]),
|
||||||
|
NeighborRouterID: binary.BigEndian.Uint32(data[j+12 : j+16]),
|
||||||
|
}
|
||||||
|
routers = append(routers, router)
|
||||||
|
}
|
||||||
|
content = RouterLSA{
|
||||||
|
Flags: uint8(data[20]),
|
||||||
|
Options: binary.BigEndian.Uint32(data[20:24]) & 0x00FFFFFF,
|
||||||
|
Routers: routers,
|
||||||
|
}
|
||||||
|
case NetworkLSAtype:
|
||||||
|
var routers []uint32
|
||||||
|
var j uint32
|
||||||
|
for j = 24; j < uint32(lsalength); j += 4 {
|
||||||
|
routers = append(routers, binary.BigEndian.Uint32(data[j:j+4]))
|
||||||
|
}
|
||||||
|
content = NetworkLSA{
|
||||||
|
Options: binary.BigEndian.Uint32(data[20:24]) & 0x00FFFFFF,
|
||||||
|
AttachedRouter: routers,
|
||||||
|
}
|
||||||
|
case InterAreaPrefixLSAtype:
|
||||||
|
content = InterAreaPrefixLSA{
|
||||||
|
Metric: binary.BigEndian.Uint32(data[20:24]) & 0x00FFFFFF,
|
||||||
|
PrefixLength: uint8(data[24]),
|
||||||
|
PrefixOptions: uint8(data[25]),
|
||||||
|
AddressPrefix: data[28:uint32(lsalength)],
|
||||||
|
}
|
||||||
|
case InterAreaRouterLSAtype:
|
||||||
|
content = InterAreaRouterLSA{
|
||||||
|
Options: binary.BigEndian.Uint32(data[20:24]) & 0x00FFFFFF,
|
||||||
|
Metric: binary.BigEndian.Uint32(data[24:28]) & 0x00FFFFFF,
|
||||||
|
DestinationRouterID: binary.BigEndian.Uint32(data[28:32]),
|
||||||
|
}
|
||||||
|
case ASExternalLSAtype:
|
||||||
|
fallthrough
|
||||||
|
case NSSALSAtype:
|
||||||
|
flags := uint8(data[20])
|
||||||
|
prefixLen := uint8(data[24]) / 8
|
||||||
|
var forwardingAddress []byte
|
||||||
|
if (flags & 0x02) == 0x02 {
|
||||||
|
forwardingAddress = data[28+uint32(prefixLen) : 28+uint32(prefixLen)+16]
|
||||||
|
}
|
||||||
|
content = ASExternalLSA{
|
||||||
|
Flags: flags,
|
||||||
|
Metric: binary.BigEndian.Uint32(data[20:24]) & 0x00FFFFFF,
|
||||||
|
PrefixLength: prefixLen,
|
||||||
|
PrefixOptions: uint8(data[25]),
|
||||||
|
RefLSType: binary.BigEndian.Uint16(data[26:28]),
|
||||||
|
AddressPrefix: data[28 : 28+uint32(prefixLen)],
|
||||||
|
ForwardingAddress: forwardingAddress,
|
||||||
|
}
|
||||||
|
case LinkLSAtype:
|
||||||
|
var prefixes []Prefix
|
||||||
|
var prefixOffset uint32 = 44
|
||||||
|
var j uint32
|
||||||
|
numOfPrefixes := binary.BigEndian.Uint32(data[40:44])
|
||||||
|
for j = 0; j < numOfPrefixes; j++ {
|
||||||
|
prefixLen := uint8(data[prefixOffset])
|
||||||
|
prefix := Prefix{
|
||||||
|
PrefixLength: prefixLen,
|
||||||
|
PrefixOptions: uint8(data[prefixOffset+1]),
|
||||||
|
AddressPrefix: data[prefixOffset+4 : prefixOffset+4+uint32(prefixLen)/8],
|
||||||
|
}
|
||||||
|
prefixes = append(prefixes, prefix)
|
||||||
|
prefixOffset = prefixOffset + 4 + uint32(prefixLen)/8
|
||||||
|
}
|
||||||
|
content = LinkLSA{
|
||||||
|
RtrPriority: uint8(data[20]),
|
||||||
|
Options: binary.BigEndian.Uint32(data[20:24]) & 0x00FFFFFF,
|
||||||
|
LinkLocalAddress: data[24:40],
|
||||||
|
NumOfPrefixes: numOfPrefixes,
|
||||||
|
Prefixes: prefixes,
|
||||||
|
}
|
||||||
|
case IntraAreaPrefixLSAtype:
|
||||||
|
var prefixes []Prefix
|
||||||
|
var prefixOffset uint32 = 32
|
||||||
|
var j uint16
|
||||||
|
numOfPrefixes := binary.BigEndian.Uint16(data[20:22])
|
||||||
|
for j = 0; j < numOfPrefixes; j++ {
|
||||||
|
prefixLen := uint8(data[prefixOffset])
|
||||||
|
prefix := Prefix{
|
||||||
|
PrefixLength: prefixLen,
|
||||||
|
PrefixOptions: uint8(data[prefixOffset+1]),
|
||||||
|
Metric: binary.BigEndian.Uint16(data[prefixOffset+2 : prefixOffset+4]),
|
||||||
|
AddressPrefix: data[prefixOffset+4 : prefixOffset+4+uint32(prefixLen)/8],
|
||||||
|
}
|
||||||
|
prefixes = append(prefixes, prefix)
|
||||||
|
prefixOffset = prefixOffset + 4 + uint32(prefixLen)
|
||||||
|
}
|
||||||
|
content = IntraAreaPrefixLSA{
|
||||||
|
NumOfPrefixes: numOfPrefixes,
|
||||||
|
RefLSType: binary.BigEndian.Uint16(data[22:24]),
|
||||||
|
RefLinkStateID: binary.BigEndian.Uint32(data[24:28]),
|
||||||
|
RefAdvRouter: binary.BigEndian.Uint32(data[28:32]),
|
||||||
|
Prefixes: prefixes,
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("Unknown Link State type.")
|
||||||
|
}
|
||||||
|
return content, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// getLSAs parses the LSA information from the packet for OSPFv3
|
||||||
|
func getLSAs(num uint32, data []byte) ([]LSA, error) {
|
||||||
|
var lsas []LSA
|
||||||
|
var i uint32 = 0
|
||||||
|
var offset uint32 = 0
|
||||||
|
for ; i < num; i++ {
|
||||||
|
var content interface{}
|
||||||
|
lstype := binary.BigEndian.Uint16(data[offset+2 : offset+4])
|
||||||
|
lsalength := binary.BigEndian.Uint16(data[offset+18 : offset+20])
|
||||||
|
|
||||||
|
content, err := extractLSAInformation(lstype, lsalength, data[offset:])
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("Could not extract Link State type.")
|
||||||
|
}
|
||||||
|
lsa := LSA{
|
||||||
|
LSAheader: LSAheader{
|
||||||
|
LSAge: binary.BigEndian.Uint16(data[offset : offset+2]),
|
||||||
|
LSType: lstype,
|
||||||
|
LinkStateID: binary.BigEndian.Uint32(data[offset+4 : offset+8]),
|
||||||
|
AdvRouter: binary.BigEndian.Uint32(data[offset+8 : offset+12]),
|
||||||
|
LSSeqNumber: binary.BigEndian.Uint32(data[offset+12 : offset+16]),
|
||||||
|
LSChecksum: binary.BigEndian.Uint16(data[offset+16 : offset+18]),
|
||||||
|
Length: lsalength,
|
||||||
|
},
|
||||||
|
Content: content,
|
||||||
|
}
|
||||||
|
lsas = append(lsas, lsa)
|
||||||
|
offset += uint32(lsalength)
|
||||||
|
}
|
||||||
|
return lsas, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DecodeFromBytes decodes the given bytes into the OSPF layer.
|
||||||
|
func (ospf *OSPFv2) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
|
||||||
|
if len(data) < 24 {
|
||||||
|
return fmt.Errorf("Packet too smal for OSPF Version 2")
|
||||||
|
}
|
||||||
|
|
||||||
|
ospf.Version = uint8(data[0])
|
||||||
|
ospf.Type = OSPFType(data[1])
|
||||||
|
ospf.PacketLength = binary.BigEndian.Uint16(data[2:4])
|
||||||
|
ospf.RouterID = binary.BigEndian.Uint32(data[4:8])
|
||||||
|
ospf.AreaID = binary.BigEndian.Uint32(data[8:12])
|
||||||
|
ospf.Checksum = binary.BigEndian.Uint16(data[12:14])
|
||||||
|
ospf.AuType = binary.BigEndian.Uint16(data[14:16])
|
||||||
|
ospf.Authentication = binary.BigEndian.Uint64(data[16:24])
|
||||||
|
|
||||||
|
switch ospf.Type {
|
||||||
|
case OSPFHello:
|
||||||
|
var neighbors []uint32
|
||||||
|
for i := 44; uint16(i+4) <= ospf.PacketLength; i += 4 {
|
||||||
|
neighbors = append(neighbors, binary.BigEndian.Uint32(data[i:i+4]))
|
||||||
|
}
|
||||||
|
ospf.Content = HelloPkgV2{
|
||||||
|
NetworkMask: binary.BigEndian.Uint32(data[24:28]),
|
||||||
|
HelloPkg: HelloPkg{
|
||||||
|
HelloInterval: binary.BigEndian.Uint16(data[28:30]),
|
||||||
|
Options: uint32(data[30]),
|
||||||
|
RtrPriority: uint8(data[31]),
|
||||||
|
RouterDeadInterval: binary.BigEndian.Uint32(data[32:36]),
|
||||||
|
DesignatedRouterID: binary.BigEndian.Uint32(data[36:40]),
|
||||||
|
BackupDesignatedRouterID: binary.BigEndian.Uint32(data[40:44]),
|
||||||
|
NeighborID: neighbors,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
case OSPFDatabaseDescription:
|
||||||
|
var lsas []LSAheader
|
||||||
|
for i := 32; uint16(i+20) <= ospf.PacketLength; i += 20 {
|
||||||
|
lsa := LSAheader{
|
||||||
|
LSAge: binary.BigEndian.Uint16(data[i : i+2]),
|
||||||
|
LSType: binary.BigEndian.Uint16(data[i+2 : i+4]),
|
||||||
|
LinkStateID: binary.BigEndian.Uint32(data[i+4 : i+8]),
|
||||||
|
AdvRouter: binary.BigEndian.Uint32(data[i+8 : i+12]),
|
||||||
|
LSSeqNumber: binary.BigEndian.Uint32(data[i+12 : i+16]),
|
||||||
|
LSChecksum: binary.BigEndian.Uint16(data[i+16 : i+18]),
|
||||||
|
Length: binary.BigEndian.Uint16(data[i+18 : i+20]),
|
||||||
|
}
|
||||||
|
lsas = append(lsas, lsa)
|
||||||
|
}
|
||||||
|
ospf.Content = DbDescPkg{
|
||||||
|
InterfaceMTU: binary.BigEndian.Uint16(data[24:26]),
|
||||||
|
Options: uint32(data[26]),
|
||||||
|
Flags: uint16(data[27]),
|
||||||
|
DDSeqNumber: binary.BigEndian.Uint32(data[28:32]),
|
||||||
|
LSAinfo: lsas,
|
||||||
|
}
|
||||||
|
case OSPFLinkStateRequest:
|
||||||
|
var lsrs []LSReq
|
||||||
|
for i := 24; uint16(i+12) <= ospf.PacketLength; i += 12 {
|
||||||
|
lsr := LSReq{
|
||||||
|
LSType: binary.BigEndian.Uint16(data[i+2 : i+4]),
|
||||||
|
LSID: binary.BigEndian.Uint32(data[i+4 : i+8]),
|
||||||
|
AdvRouter: binary.BigEndian.Uint32(data[i+8 : i+12]),
|
||||||
|
}
|
||||||
|
lsrs = append(lsrs, lsr)
|
||||||
|
}
|
||||||
|
ospf.Content = lsrs
|
||||||
|
case OSPFLinkStateUpdate:
|
||||||
|
num := binary.BigEndian.Uint32(data[24:28])
|
||||||
|
|
||||||
|
lsas, err := getLSAsv2(num, data[28:])
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Cannot parse Link State Update packet: %v", err)
|
||||||
|
}
|
||||||
|
ospf.Content = LSUpdate{
|
||||||
|
NumOfLSAs: num,
|
||||||
|
LSAs: lsas,
|
||||||
|
}
|
||||||
|
case OSPFLinkStateAcknowledgment:
|
||||||
|
var lsas []LSAheader
|
||||||
|
for i := 24; uint16(i+20) <= ospf.PacketLength; i += 20 {
|
||||||
|
lsa := LSAheader{
|
||||||
|
LSAge: binary.BigEndian.Uint16(data[i : i+2]),
|
||||||
|
LSOptions: data[i+2],
|
||||||
|
LSType: uint16(data[i+3]),
|
||||||
|
LinkStateID: binary.BigEndian.Uint32(data[i+4 : i+8]),
|
||||||
|
AdvRouter: binary.BigEndian.Uint32(data[i+8 : i+12]),
|
||||||
|
LSSeqNumber: binary.BigEndian.Uint32(data[i+12 : i+16]),
|
||||||
|
LSChecksum: binary.BigEndian.Uint16(data[i+16 : i+18]),
|
||||||
|
Length: binary.BigEndian.Uint16(data[i+18 : i+20]),
|
||||||
|
}
|
||||||
|
lsas = append(lsas, lsa)
|
||||||
|
}
|
||||||
|
ospf.Content = lsas
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DecodeFromBytes decodes the given bytes into the OSPF layer.
|
||||||
|
func (ospf *OSPFv3) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
|
||||||
|
|
||||||
|
if len(data) < 16 {
|
||||||
|
return fmt.Errorf("Packet too smal for OSPF Version 3")
|
||||||
|
}
|
||||||
|
|
||||||
|
ospf.Version = uint8(data[0])
|
||||||
|
ospf.Type = OSPFType(data[1])
|
||||||
|
ospf.PacketLength = binary.BigEndian.Uint16(data[2:4])
|
||||||
|
ospf.RouterID = binary.BigEndian.Uint32(data[4:8])
|
||||||
|
ospf.AreaID = binary.BigEndian.Uint32(data[8:12])
|
||||||
|
ospf.Checksum = binary.BigEndian.Uint16(data[12:14])
|
||||||
|
ospf.Instance = uint8(data[14])
|
||||||
|
ospf.Reserved = uint8(data[15])
|
||||||
|
|
||||||
|
switch ospf.Type {
|
||||||
|
case OSPFHello:
|
||||||
|
var neighbors []uint32
|
||||||
|
for i := 36; uint16(i+4) <= ospf.PacketLength; i += 4 {
|
||||||
|
neighbors = append(neighbors, binary.BigEndian.Uint32(data[i:i+4]))
|
||||||
|
}
|
||||||
|
ospf.Content = HelloPkg{
|
||||||
|
InterfaceID: binary.BigEndian.Uint32(data[16:20]),
|
||||||
|
RtrPriority: uint8(data[20]),
|
||||||
|
Options: binary.BigEndian.Uint32(data[21:25]) >> 8,
|
||||||
|
HelloInterval: binary.BigEndian.Uint16(data[24:26]),
|
||||||
|
RouterDeadInterval: uint32(binary.BigEndian.Uint16(data[26:28])),
|
||||||
|
DesignatedRouterID: binary.BigEndian.Uint32(data[28:32]),
|
||||||
|
BackupDesignatedRouterID: binary.BigEndian.Uint32(data[32:36]),
|
||||||
|
NeighborID: neighbors,
|
||||||
|
}
|
||||||
|
case OSPFDatabaseDescription:
|
||||||
|
var lsas []LSAheader
|
||||||
|
for i := 28; uint16(i+20) <= ospf.PacketLength; i += 20 {
|
||||||
|
lsa := LSAheader{
|
||||||
|
LSAge: binary.BigEndian.Uint16(data[i : i+2]),
|
||||||
|
LSType: binary.BigEndian.Uint16(data[i+2 : i+4]),
|
||||||
|
LinkStateID: binary.BigEndian.Uint32(data[i+4 : i+8]),
|
||||||
|
AdvRouter: binary.BigEndian.Uint32(data[i+8 : i+12]),
|
||||||
|
LSSeqNumber: binary.BigEndian.Uint32(data[i+12 : i+16]),
|
||||||
|
LSChecksum: binary.BigEndian.Uint16(data[i+16 : i+18]),
|
||||||
|
Length: binary.BigEndian.Uint16(data[i+18 : i+20]),
|
||||||
|
}
|
||||||
|
lsas = append(lsas, lsa)
|
||||||
|
}
|
||||||
|
ospf.Content = DbDescPkg{
|
||||||
|
Options: binary.BigEndian.Uint32(data[16:20]) & 0x00FFFFFF,
|
||||||
|
InterfaceMTU: binary.BigEndian.Uint16(data[20:22]),
|
||||||
|
Flags: binary.BigEndian.Uint16(data[22:24]),
|
||||||
|
DDSeqNumber: binary.BigEndian.Uint32(data[24:28]),
|
||||||
|
LSAinfo: lsas,
|
||||||
|
}
|
||||||
|
case OSPFLinkStateRequest:
|
||||||
|
var lsrs []LSReq
|
||||||
|
for i := 16; uint16(i+12) <= ospf.PacketLength; i += 12 {
|
||||||
|
lsr := LSReq{
|
||||||
|
LSType: binary.BigEndian.Uint16(data[i+2 : i+4]),
|
||||||
|
LSID: binary.BigEndian.Uint32(data[i+4 : i+8]),
|
||||||
|
AdvRouter: binary.BigEndian.Uint32(data[i+8 : i+12]),
|
||||||
|
}
|
||||||
|
lsrs = append(lsrs, lsr)
|
||||||
|
}
|
||||||
|
ospf.Content = lsrs
|
||||||
|
case OSPFLinkStateUpdate:
|
||||||
|
num := binary.BigEndian.Uint32(data[16:20])
|
||||||
|
lsas, err := getLSAs(num, data[20:])
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Cannot parse Link State Update packet: %v", err)
|
||||||
|
}
|
||||||
|
ospf.Content = LSUpdate{
|
||||||
|
NumOfLSAs: num,
|
||||||
|
LSAs: lsas,
|
||||||
|
}
|
||||||
|
|
||||||
|
case OSPFLinkStateAcknowledgment:
|
||||||
|
var lsas []LSAheader
|
||||||
|
for i := 16; uint16(i+20) <= ospf.PacketLength; i += 20 {
|
||||||
|
lsa := LSAheader{
|
||||||
|
LSAge: binary.BigEndian.Uint16(data[i : i+2]),
|
||||||
|
LSType: binary.BigEndian.Uint16(data[i+2 : i+4]),
|
||||||
|
LinkStateID: binary.BigEndian.Uint32(data[i+4 : i+8]),
|
||||||
|
AdvRouter: binary.BigEndian.Uint32(data[i+8 : i+12]),
|
||||||
|
LSSeqNumber: binary.BigEndian.Uint32(data[i+12 : i+16]),
|
||||||
|
LSChecksum: binary.BigEndian.Uint16(data[i+16 : i+18]),
|
||||||
|
Length: binary.BigEndian.Uint16(data[i+18 : i+20]),
|
||||||
|
}
|
||||||
|
lsas = append(lsas, lsa)
|
||||||
|
}
|
||||||
|
ospf.Content = lsas
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// LayerType returns LayerTypeOSPF
|
||||||
|
func (ospf *OSPFv2) LayerType() gopacket.LayerType {
|
||||||
|
return LayerTypeOSPF
|
||||||
|
}
|
||||||
|
func (ospf *OSPFv3) LayerType() gopacket.LayerType {
|
||||||
|
return LayerTypeOSPF
|
||||||
|
}
|
||||||
|
|
||||||
|
// NextLayerType returns the layer type contained by this DecodingLayer.
|
||||||
|
func (ospf *OSPFv2) NextLayerType() gopacket.LayerType {
|
||||||
|
return gopacket.LayerTypeZero
|
||||||
|
}
|
||||||
|
func (ospf *OSPFv3) NextLayerType() gopacket.LayerType {
|
||||||
|
return gopacket.LayerTypeZero
|
||||||
|
}
|
||||||
|
|
||||||
|
// CanDecode returns the set of layer types that this DecodingLayer can decode.
|
||||||
|
func (ospf *OSPFv2) CanDecode() gopacket.LayerClass {
|
||||||
|
return LayerTypeOSPF
|
||||||
|
}
|
||||||
|
func (ospf *OSPFv3) CanDecode() gopacket.LayerClass {
|
||||||
|
return LayerTypeOSPF
|
||||||
|
}
|
||||||
|
|
||||||
|
func decodeOSPF(data []byte, p gopacket.PacketBuilder) error {
|
||||||
|
if len(data) < 14 {
|
||||||
|
return fmt.Errorf("Packet too smal for OSPF")
|
||||||
|
}
|
||||||
|
|
||||||
|
switch uint8(data[0]) {
|
||||||
|
case 2:
|
||||||
|
ospf := &OSPFv2{}
|
||||||
|
return decodingLayerDecoder(ospf, data, p)
|
||||||
|
case 3:
|
||||||
|
ospf := &OSPFv3{}
|
||||||
|
return decodingLayerDecoder(ospf, data, p)
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Errorf("Unable to determine OSPF type.")
|
||||||
|
}
|
|
@ -0,0 +1,84 @@
|
||||||
|
// 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"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/google/gopacket"
|
||||||
|
)
|
||||||
|
|
||||||
|
type PFDirection uint8
|
||||||
|
|
||||||
|
const (
|
||||||
|
PFDirectionInOut PFDirection = 0
|
||||||
|
PFDirectionIn PFDirection = 1
|
||||||
|
PFDirectionOut PFDirection = 2
|
||||||
|
)
|
||||||
|
|
||||||
|
// PFLog provides the layer for 'pf' packet-filter logging, as described at
|
||||||
|
// http://www.freebsd.org/cgi/man.cgi?query=pflog&sektion=4
|
||||||
|
type PFLog struct {
|
||||||
|
BaseLayer
|
||||||
|
Length uint8
|
||||||
|
Family ProtocolFamily
|
||||||
|
Action, Reason uint8
|
||||||
|
IFName, Ruleset []byte
|
||||||
|
RuleNum, SubruleNum uint32
|
||||||
|
UID uint32
|
||||||
|
PID int32
|
||||||
|
RuleUID uint32
|
||||||
|
RulePID int32
|
||||||
|
Direction PFDirection
|
||||||
|
// The remainder is padding
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pf *PFLog) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
|
||||||
|
if len(data) < 60 {
|
||||||
|
df.SetTruncated()
|
||||||
|
return errors.New("PFLog data less than 60 bytes")
|
||||||
|
}
|
||||||
|
pf.Length = data[0]
|
||||||
|
pf.Family = ProtocolFamily(data[1])
|
||||||
|
pf.Action = data[2]
|
||||||
|
pf.Reason = data[3]
|
||||||
|
pf.IFName = data[4:20]
|
||||||
|
pf.Ruleset = data[20:36]
|
||||||
|
pf.RuleNum = binary.BigEndian.Uint32(data[36:40])
|
||||||
|
pf.SubruleNum = binary.BigEndian.Uint32(data[40:44])
|
||||||
|
pf.UID = binary.BigEndian.Uint32(data[44:48])
|
||||||
|
pf.PID = int32(binary.BigEndian.Uint32(data[48:52]))
|
||||||
|
pf.RuleUID = binary.BigEndian.Uint32(data[52:56])
|
||||||
|
pf.RulePID = int32(binary.BigEndian.Uint32(data[56:60]))
|
||||||
|
pf.Direction = PFDirection(data[60])
|
||||||
|
if pf.Length%4 != 1 {
|
||||||
|
return errors.New("PFLog header length should be 3 less than multiple of 4")
|
||||||
|
}
|
||||||
|
actualLength := int(pf.Length) + 3
|
||||||
|
if len(data) < actualLength {
|
||||||
|
return fmt.Errorf("PFLog data size < %d", actualLength)
|
||||||
|
}
|
||||||
|
pf.Contents = data[:actualLength]
|
||||||
|
pf.Payload = data[actualLength:]
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// LayerType returns layers.LayerTypePFLog
|
||||||
|
func (pf *PFLog) LayerType() gopacket.LayerType { return LayerTypePFLog }
|
||||||
|
|
||||||
|
func (pf *PFLog) CanDecode() gopacket.LayerClass { return LayerTypePFLog }
|
||||||
|
|
||||||
|
func (pf *PFLog) NextLayerType() gopacket.LayerType {
|
||||||
|
return pf.Family.LayerType()
|
||||||
|
}
|
||||||
|
|
||||||
|
func decodePFLog(data []byte, p gopacket.PacketBuilder) error {
|
||||||
|
pf := &PFLog{}
|
||||||
|
return decodingLayerDecoder(pf, data, p)
|
||||||
|
}
|
|
@ -0,0 +1,156 @@
|
||||||
|
// 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 (
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/google/gopacket"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TCPPort is a port in a TCP layer.
|
||||||
|
type TCPPort uint16
|
||||||
|
|
||||||
|
// UDPPort is a port in a UDP layer.
|
||||||
|
type UDPPort uint16
|
||||||
|
|
||||||
|
// RUDPPort is a port in a RUDP layer.
|
||||||
|
type RUDPPort uint8
|
||||||
|
|
||||||
|
// SCTPPort is a port in a SCTP layer.
|
||||||
|
type SCTPPort uint16
|
||||||
|
|
||||||
|
// UDPLitePort is a port in a UDPLite layer.
|
||||||
|
type UDPLitePort uint16
|
||||||
|
|
||||||
|
// RUDPPortNames contains the string names for all RUDP ports.
|
||||||
|
var RUDPPortNames = map[RUDPPort]string{}
|
||||||
|
|
||||||
|
// UDPLitePortNames contains the string names for all UDPLite ports.
|
||||||
|
var UDPLitePortNames = map[UDPLitePort]string{}
|
||||||
|
|
||||||
|
// {TCP,UDP,SCTP}PortNames can be found in iana_ports.go
|
||||||
|
|
||||||
|
// String returns the port as "number(name)" if there's a well-known port name,
|
||||||
|
// or just "number" if there isn't. Well-known names are stored in
|
||||||
|
// TCPPortNames.
|
||||||
|
func (a TCPPort) String() string {
|
||||||
|
if name, ok := TCPPortNames[a]; ok {
|
||||||
|
return fmt.Sprintf("%d(%s)", a, name)
|
||||||
|
}
|
||||||
|
return strconv.Itoa(int(a))
|
||||||
|
}
|
||||||
|
|
||||||
|
// LayerType returns a LayerType that would be able to decode the
|
||||||
|
// application payload. It uses some well-known ports such as 53 for
|
||||||
|
// DNS.
|
||||||
|
//
|
||||||
|
// Returns gopacket.LayerTypePayload for unknown/unsupported port numbers.
|
||||||
|
func (a TCPPort) LayerType() gopacket.LayerType {
|
||||||
|
lt := tcpPortLayerType[uint16(a)]
|
||||||
|
if lt != 0 {
|
||||||
|
return lt
|
||||||
|
}
|
||||||
|
return gopacket.LayerTypePayload
|
||||||
|
}
|
||||||
|
|
||||||
|
var tcpPortLayerType = [65536]gopacket.LayerType{
|
||||||
|
53: LayerTypeDNS,
|
||||||
|
443: LayerTypeTLS, // https
|
||||||
|
502: LayerTypeModbusTCP, // modbustcp
|
||||||
|
636: LayerTypeTLS, // ldaps
|
||||||
|
989: LayerTypeTLS, // ftps-data
|
||||||
|
990: LayerTypeTLS, // ftps
|
||||||
|
992: LayerTypeTLS, // telnets
|
||||||
|
993: LayerTypeTLS, // imaps
|
||||||
|
994: LayerTypeTLS, // ircs
|
||||||
|
995: LayerTypeTLS, // pop3s
|
||||||
|
5061: LayerTypeTLS, // ips
|
||||||
|
}
|
||||||
|
|
||||||
|
// RegisterTCPPortLayerType creates a new mapping between a TCPPort
|
||||||
|
// and an underlaying LayerType.
|
||||||
|
func RegisterTCPPortLayerType(port TCPPort, layerType gopacket.LayerType) {
|
||||||
|
tcpPortLayerType[port] = layerType
|
||||||
|
}
|
||||||
|
|
||||||
|
// String returns the port as "number(name)" if there's a well-known port name,
|
||||||
|
// or just "number" if there isn't. Well-known names are stored in
|
||||||
|
// UDPPortNames.
|
||||||
|
func (a UDPPort) String() string {
|
||||||
|
if name, ok := UDPPortNames[a]; ok {
|
||||||
|
return fmt.Sprintf("%d(%s)", a, name)
|
||||||
|
}
|
||||||
|
return strconv.Itoa(int(a))
|
||||||
|
}
|
||||||
|
|
||||||
|
// LayerType returns a LayerType that would be able to decode the
|
||||||
|
// application payload. It uses some well-known ports such as 53 for
|
||||||
|
// DNS.
|
||||||
|
//
|
||||||
|
// Returns gopacket.LayerTypePayload for unknown/unsupported port numbers.
|
||||||
|
func (a UDPPort) LayerType() gopacket.LayerType {
|
||||||
|
lt := udpPortLayerType[uint16(a)]
|
||||||
|
if lt != 0 {
|
||||||
|
return lt
|
||||||
|
}
|
||||||
|
return gopacket.LayerTypePayload
|
||||||
|
}
|
||||||
|
|
||||||
|
var udpPortLayerType = [65536]gopacket.LayerType{
|
||||||
|
53: LayerTypeDNS,
|
||||||
|
123: LayerTypeNTP,
|
||||||
|
4789: LayerTypeVXLAN,
|
||||||
|
67: LayerTypeDHCPv4,
|
||||||
|
68: LayerTypeDHCPv4,
|
||||||
|
546: LayerTypeDHCPv6,
|
||||||
|
547: LayerTypeDHCPv6,
|
||||||
|
5060: LayerTypeSIP,
|
||||||
|
6343: LayerTypeSFlow,
|
||||||
|
6081: LayerTypeGeneve,
|
||||||
|
3784: LayerTypeBFD,
|
||||||
|
2152: LayerTypeGTPv1U,
|
||||||
|
623: LayerTypeRMCP,
|
||||||
|
1812: LayerTypeRADIUS,
|
||||||
|
}
|
||||||
|
|
||||||
|
// RegisterUDPPortLayerType creates a new mapping between a UDPPort
|
||||||
|
// and an underlaying LayerType.
|
||||||
|
func RegisterUDPPortLayerType(port UDPPort, layerType gopacket.LayerType) {
|
||||||
|
udpPortLayerType[port] = layerType
|
||||||
|
}
|
||||||
|
|
||||||
|
// String returns the port as "number(name)" if there's a well-known port name,
|
||||||
|
// or just "number" if there isn't. Well-known names are stored in
|
||||||
|
// RUDPPortNames.
|
||||||
|
func (a RUDPPort) String() string {
|
||||||
|
if name, ok := RUDPPortNames[a]; ok {
|
||||||
|
return fmt.Sprintf("%d(%s)", a, name)
|
||||||
|
}
|
||||||
|
return strconv.Itoa(int(a))
|
||||||
|
}
|
||||||
|
|
||||||
|
// String returns the port as "number(name)" if there's a well-known port name,
|
||||||
|
// or just "number" if there isn't. Well-known names are stored in
|
||||||
|
// SCTPPortNames.
|
||||||
|
func (a SCTPPort) String() string {
|
||||||
|
if name, ok := SCTPPortNames[a]; ok {
|
||||||
|
return fmt.Sprintf("%d(%s)", a, name)
|
||||||
|
}
|
||||||
|
return strconv.Itoa(int(a))
|
||||||
|
}
|
||||||
|
|
||||||
|
// String returns the port as "number(name)" if there's a well-known port name,
|
||||||
|
// or just "number" if there isn't. Well-known names are stored in
|
||||||
|
// UDPLitePortNames.
|
||||||
|
func (a UDPLitePort) String() string {
|
||||||
|
if name, ok := UDPLitePortNames[a]; ok {
|
||||||
|
return fmt.Sprintf("%d(%s)", a, name)
|
||||||
|
}
|
||||||
|
return strconv.Itoa(int(a))
|
||||||
|
}
|
|
@ -0,0 +1,88 @@
|
||||||
|
// 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"
|
||||||
|
)
|
||||||
|
|
||||||
|
// PPP is the layer for PPP encapsulation headers.
|
||||||
|
type PPP struct {
|
||||||
|
BaseLayer
|
||||||
|
PPPType PPPType
|
||||||
|
HasPPTPHeader bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// PPPEndpoint is a singleton endpoint for PPP. Since there is no actual
|
||||||
|
// addressing for the two ends of a PPP connection, we use a singleton value
|
||||||
|
// named 'point' for each endpoint.
|
||||||
|
var PPPEndpoint = gopacket.NewEndpoint(EndpointPPP, nil)
|
||||||
|
|
||||||
|
// PPPFlow is a singleton flow for PPP. Since there is no actual addressing for
|
||||||
|
// the two ends of a PPP connection, we use a singleton value to represent the
|
||||||
|
// flow for all PPP connections.
|
||||||
|
var PPPFlow = gopacket.NewFlow(EndpointPPP, nil, nil)
|
||||||
|
|
||||||
|
// LayerType returns LayerTypePPP
|
||||||
|
func (p *PPP) LayerType() gopacket.LayerType { return LayerTypePPP }
|
||||||
|
|
||||||
|
// LinkFlow returns PPPFlow.
|
||||||
|
func (p *PPP) LinkFlow() gopacket.Flow { return PPPFlow }
|
||||||
|
|
||||||
|
func decodePPP(data []byte, p gopacket.PacketBuilder) error {
|
||||||
|
ppp := &PPP{}
|
||||||
|
offset := 0
|
||||||
|
if data[0] == 0xff && data[1] == 0x03 {
|
||||||
|
offset = 2
|
||||||
|
ppp.HasPPTPHeader = true
|
||||||
|
}
|
||||||
|
if data[offset]&0x1 == 0 {
|
||||||
|
if data[offset+1]&0x1 == 0 {
|
||||||
|
return errors.New("PPP has invalid type")
|
||||||
|
}
|
||||||
|
ppp.PPPType = PPPType(binary.BigEndian.Uint16(data[offset : offset+2]))
|
||||||
|
ppp.Contents = data[offset : offset+2]
|
||||||
|
ppp.Payload = data[offset+2:]
|
||||||
|
} else {
|
||||||
|
ppp.PPPType = PPPType(data[offset])
|
||||||
|
ppp.Contents = data[offset : offset+1]
|
||||||
|
ppp.Payload = data[offset+1:]
|
||||||
|
}
|
||||||
|
p.AddLayer(ppp)
|
||||||
|
p.SetLinkLayer(ppp)
|
||||||
|
return p.NextDecoder(ppp.PPPType)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 *PPP) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
|
||||||
|
if p.PPPType&0x100 == 0 {
|
||||||
|
bytes, err := b.PrependBytes(2)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
binary.BigEndian.PutUint16(bytes, uint16(p.PPPType))
|
||||||
|
} else {
|
||||||
|
bytes, err := b.PrependBytes(1)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
bytes[0] = uint8(p.PPPType)
|
||||||
|
}
|
||||||
|
if p.HasPPTPHeader {
|
||||||
|
bytes, err := b.PrependBytes(2)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
bytes[0] = 0xff
|
||||||
|
bytes[1] = 0x03
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,60 @@
|
||||||
|
// 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"
|
||||||
|
)
|
||||||
|
|
||||||
|
// PPPoE is the layer for PPPoE encapsulation headers.
|
||||||
|
type PPPoE struct {
|
||||||
|
BaseLayer
|
||||||
|
Version uint8
|
||||||
|
Type uint8
|
||||||
|
Code PPPoECode
|
||||||
|
SessionId uint16
|
||||||
|
Length uint16
|
||||||
|
}
|
||||||
|
|
||||||
|
// LayerType returns gopacket.LayerTypePPPoE.
|
||||||
|
func (p *PPPoE) LayerType() gopacket.LayerType {
|
||||||
|
return LayerTypePPPoE
|
||||||
|
}
|
||||||
|
|
||||||
|
// decodePPPoE decodes the PPPoE header (see http://tools.ietf.org/html/rfc2516).
|
||||||
|
func decodePPPoE(data []byte, p gopacket.PacketBuilder) error {
|
||||||
|
pppoe := &PPPoE{
|
||||||
|
Version: data[0] >> 4,
|
||||||
|
Type: data[0] & 0x0F,
|
||||||
|
Code: PPPoECode(data[1]),
|
||||||
|
SessionId: binary.BigEndian.Uint16(data[2:4]),
|
||||||
|
Length: binary.BigEndian.Uint16(data[4:6]),
|
||||||
|
}
|
||||||
|
pppoe.BaseLayer = BaseLayer{data[:6], data[6 : 6+pppoe.Length]}
|
||||||
|
p.AddLayer(pppoe)
|
||||||
|
return p.NextDecoder(pppoe.Code)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 *PPPoE) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
|
||||||
|
payload := b.Bytes()
|
||||||
|
bytes, err := b.PrependBytes(6)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
bytes[0] = (p.Version << 4) | p.Type
|
||||||
|
bytes[1] = byte(p.Code)
|
||||||
|
binary.BigEndian.PutUint16(bytes[2:], p.SessionId)
|
||||||
|
if opts.FixLengths {
|
||||||
|
p.Length = uint16(len(payload))
|
||||||
|
}
|
||||||
|
binary.BigEndian.PutUint16(bytes[4:], p.Length)
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,146 @@
|
||||||
|
// Copyright 2015 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.
|
||||||
|
|
||||||
|
// http://www.tcpdump.org/linktypes/LINKTYPE_IEEE802_11_PRISM.html
|
||||||
|
|
||||||
|
package layers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/binary"
|
||||||
|
"errors"
|
||||||
|
|
||||||
|
"github.com/google/gopacket"
|
||||||
|
)
|
||||||
|
|
||||||
|
func decodePrismValue(data []byte, pv *PrismValue) {
|
||||||
|
pv.DID = PrismDID(binary.LittleEndian.Uint32(data[0:4]))
|
||||||
|
pv.Status = binary.LittleEndian.Uint16(data[4:6])
|
||||||
|
pv.Length = binary.LittleEndian.Uint16(data[6:8])
|
||||||
|
pv.Data = data[8 : 8+pv.Length]
|
||||||
|
}
|
||||||
|
|
||||||
|
type PrismDID uint32
|
||||||
|
|
||||||
|
const (
|
||||||
|
PrismDIDType1HostTime PrismDID = 0x10044
|
||||||
|
PrismDIDType2HostTime PrismDID = 0x01041
|
||||||
|
PrismDIDType1MACTime PrismDID = 0x20044
|
||||||
|
PrismDIDType2MACTime PrismDID = 0x02041
|
||||||
|
PrismDIDType1Channel PrismDID = 0x30044
|
||||||
|
PrismDIDType2Channel PrismDID = 0x03041
|
||||||
|
PrismDIDType1RSSI PrismDID = 0x40044
|
||||||
|
PrismDIDType2RSSI PrismDID = 0x04041
|
||||||
|
PrismDIDType1SignalQuality PrismDID = 0x50044
|
||||||
|
PrismDIDType2SignalQuality PrismDID = 0x05041
|
||||||
|
PrismDIDType1Signal PrismDID = 0x60044
|
||||||
|
PrismDIDType2Signal PrismDID = 0x06041
|
||||||
|
PrismDIDType1Noise PrismDID = 0x70044
|
||||||
|
PrismDIDType2Noise PrismDID = 0x07041
|
||||||
|
PrismDIDType1Rate PrismDID = 0x80044
|
||||||
|
PrismDIDType2Rate PrismDID = 0x08041
|
||||||
|
PrismDIDType1TransmittedFrameIndicator PrismDID = 0x90044
|
||||||
|
PrismDIDType2TransmittedFrameIndicator PrismDID = 0x09041
|
||||||
|
PrismDIDType1FrameLength PrismDID = 0xA0044
|
||||||
|
PrismDIDType2FrameLength PrismDID = 0x0A041
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
PrismType1MessageCode uint16 = 0x00000044
|
||||||
|
PrismType2MessageCode uint16 = 0x00000041
|
||||||
|
)
|
||||||
|
|
||||||
|
func (p PrismDID) String() string {
|
||||||
|
dids := map[PrismDID]string{
|
||||||
|
PrismDIDType1HostTime: "Host Time",
|
||||||
|
PrismDIDType2HostTime: "Host Time",
|
||||||
|
PrismDIDType1MACTime: "MAC Time",
|
||||||
|
PrismDIDType2MACTime: "MAC Time",
|
||||||
|
PrismDIDType1Channel: "Channel",
|
||||||
|
PrismDIDType2Channel: "Channel",
|
||||||
|
PrismDIDType1RSSI: "RSSI",
|
||||||
|
PrismDIDType2RSSI: "RSSI",
|
||||||
|
PrismDIDType1SignalQuality: "Signal Quality",
|
||||||
|
PrismDIDType2SignalQuality: "Signal Quality",
|
||||||
|
PrismDIDType1Signal: "Signal",
|
||||||
|
PrismDIDType2Signal: "Signal",
|
||||||
|
PrismDIDType1Noise: "Noise",
|
||||||
|
PrismDIDType2Noise: "Noise",
|
||||||
|
PrismDIDType1Rate: "Rate",
|
||||||
|
PrismDIDType2Rate: "Rate",
|
||||||
|
PrismDIDType1TransmittedFrameIndicator: "Transmitted Frame Indicator",
|
||||||
|
PrismDIDType2TransmittedFrameIndicator: "Transmitted Frame Indicator",
|
||||||
|
PrismDIDType1FrameLength: "Frame Length",
|
||||||
|
PrismDIDType2FrameLength: "Frame Length",
|
||||||
|
}
|
||||||
|
|
||||||
|
if str, ok := dids[p]; ok {
|
||||||
|
return str
|
||||||
|
}
|
||||||
|
|
||||||
|
return "Unknown DID"
|
||||||
|
}
|
||||||
|
|
||||||
|
type PrismValue struct {
|
||||||
|
DID PrismDID
|
||||||
|
Status uint16
|
||||||
|
Length uint16
|
||||||
|
Data []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pv *PrismValue) IsSupplied() bool {
|
||||||
|
return pv.Status == 1
|
||||||
|
}
|
||||||
|
|
||||||
|
var ErrPrismExpectedMoreData = errors.New("Expected more data.")
|
||||||
|
var ErrPrismInvalidCode = errors.New("Invalid header code.")
|
||||||
|
|
||||||
|
func decodePrismHeader(data []byte, p gopacket.PacketBuilder) error {
|
||||||
|
d := &PrismHeader{}
|
||||||
|
return decodingLayerDecoder(d, data, p)
|
||||||
|
}
|
||||||
|
|
||||||
|
type PrismHeader struct {
|
||||||
|
BaseLayer
|
||||||
|
Code uint16
|
||||||
|
Length uint16
|
||||||
|
DeviceName string
|
||||||
|
Values []PrismValue
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *PrismHeader) LayerType() gopacket.LayerType { return LayerTypePrismHeader }
|
||||||
|
|
||||||
|
func (m *PrismHeader) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
|
||||||
|
m.Code = binary.LittleEndian.Uint16(data[0:4])
|
||||||
|
m.Length = binary.LittleEndian.Uint16(data[4:8])
|
||||||
|
m.DeviceName = string(data[8:24])
|
||||||
|
m.BaseLayer = BaseLayer{Contents: data[:m.Length], Payload: data[m.Length:len(data)]}
|
||||||
|
|
||||||
|
switch m.Code {
|
||||||
|
case PrismType1MessageCode:
|
||||||
|
fallthrough
|
||||||
|
case PrismType2MessageCode:
|
||||||
|
// valid message code
|
||||||
|
default:
|
||||||
|
return ErrPrismInvalidCode
|
||||||
|
}
|
||||||
|
|
||||||
|
offset := uint16(24)
|
||||||
|
|
||||||
|
m.Values = make([]PrismValue, (m.Length-offset)/12)
|
||||||
|
for i := 0; i < len(m.Values); i++ {
|
||||||
|
decodePrismValue(data[offset:offset+12], &m.Values[i])
|
||||||
|
offset += 12
|
||||||
|
}
|
||||||
|
|
||||||
|
if offset != m.Length {
|
||||||
|
return ErrPrismExpectedMoreData
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *PrismHeader) CanDecode() gopacket.LayerClass { return LayerTypePrismHeader }
|
||||||
|
func (m *PrismHeader) NextLayerType() gopacket.LayerType { return LayerTypeDot11 }
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,560 @@
|
||||||
|
// Copyright 2020 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"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/google/gopacket"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// RFC 2865 3. Packet Format
|
||||||
|
// `The minimum length is 20 and maximum length is 4096.`
|
||||||
|
radiusMinimumRecordSizeInBytes int = 20
|
||||||
|
radiusMaximumRecordSizeInBytes int = 4096
|
||||||
|
|
||||||
|
// RFC 2865 5. Attributes
|
||||||
|
// `The Length field is one octet, and indicates the length of this Attribute including the Type, Length and Value fields.`
|
||||||
|
// `The Value field is zero or more octets and contains information specific to the Attribute.`
|
||||||
|
radiusAttributesMinimumRecordSizeInBytes int = 2
|
||||||
|
)
|
||||||
|
|
||||||
|
// RADIUS represents a Remote Authentication Dial In User Service layer.
|
||||||
|
type RADIUS struct {
|
||||||
|
BaseLayer
|
||||||
|
|
||||||
|
Code RADIUSCode
|
||||||
|
Identifier RADIUSIdentifier
|
||||||
|
Length RADIUSLength
|
||||||
|
Authenticator RADIUSAuthenticator
|
||||||
|
Attributes []RADIUSAttribute
|
||||||
|
}
|
||||||
|
|
||||||
|
// RADIUSCode represents packet type.
|
||||||
|
type RADIUSCode uint8
|
||||||
|
|
||||||
|
// constants that define RADIUSCode.
|
||||||
|
const (
|
||||||
|
RADIUSCodeAccessRequest RADIUSCode = 1 // RFC2865 3. Packet Format
|
||||||
|
RADIUSCodeAccessAccept RADIUSCode = 2 // RFC2865 3. Packet Format
|
||||||
|
RADIUSCodeAccessReject RADIUSCode = 3 // RFC2865 3. Packet Format
|
||||||
|
RADIUSCodeAccountingRequest RADIUSCode = 4 // RFC2865 3. Packet Format
|
||||||
|
RADIUSCodeAccountingResponse RADIUSCode = 5 // RFC2865 3. Packet Format
|
||||||
|
RADIUSCodeAccessChallenge RADIUSCode = 11 // RFC2865 3. Packet Format
|
||||||
|
RADIUSCodeStatusServer RADIUSCode = 12 // RFC2865 3. Packet Format (experimental)
|
||||||
|
RADIUSCodeStatusClient RADIUSCode = 13 // RFC2865 3. Packet Format (experimental)
|
||||||
|
RADIUSCodeReserved RADIUSCode = 255 // RFC2865 3. Packet Format
|
||||||
|
)
|
||||||
|
|
||||||
|
// String returns a string version of a RADIUSCode.
|
||||||
|
func (t RADIUSCode) String() (s string) {
|
||||||
|
switch t {
|
||||||
|
case RADIUSCodeAccessRequest:
|
||||||
|
s = "Access-Request"
|
||||||
|
case RADIUSCodeAccessAccept:
|
||||||
|
s = "Access-Accept"
|
||||||
|
case RADIUSCodeAccessReject:
|
||||||
|
s = "Access-Reject"
|
||||||
|
case RADIUSCodeAccountingRequest:
|
||||||
|
s = "Accounting-Request"
|
||||||
|
case RADIUSCodeAccountingResponse:
|
||||||
|
s = "Accounting-Response"
|
||||||
|
case RADIUSCodeAccessChallenge:
|
||||||
|
s = "Access-Challenge"
|
||||||
|
case RADIUSCodeStatusServer:
|
||||||
|
s = "Status-Server"
|
||||||
|
case RADIUSCodeStatusClient:
|
||||||
|
s = "Status-Client"
|
||||||
|
case RADIUSCodeReserved:
|
||||||
|
s = "Reserved"
|
||||||
|
default:
|
||||||
|
s = fmt.Sprintf("Unknown(%d)", t)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// RADIUSIdentifier represents packet identifier.
|
||||||
|
type RADIUSIdentifier uint8
|
||||||
|
|
||||||
|
// RADIUSLength represents packet length.
|
||||||
|
type RADIUSLength uint16
|
||||||
|
|
||||||
|
// RADIUSAuthenticator represents authenticator.
|
||||||
|
type RADIUSAuthenticator [16]byte
|
||||||
|
|
||||||
|
// RADIUSAttribute represents attributes.
|
||||||
|
type RADIUSAttribute struct {
|
||||||
|
Type RADIUSAttributeType
|
||||||
|
Length RADIUSAttributeLength
|
||||||
|
Value RADIUSAttributeValue
|
||||||
|
}
|
||||||
|
|
||||||
|
// RADIUSAttributeType represents attribute type.
|
||||||
|
type RADIUSAttributeType uint8
|
||||||
|
|
||||||
|
// constants that define RADIUSAttributeType.
|
||||||
|
const (
|
||||||
|
RADIUSAttributeTypeUserName RADIUSAttributeType = 1 // RFC2865 5.1. User-Name
|
||||||
|
RADIUSAttributeTypeUserPassword RADIUSAttributeType = 2 // RFC2865 5.2. User-Password
|
||||||
|
RADIUSAttributeTypeCHAPPassword RADIUSAttributeType = 3 // RFC2865 5.3. CHAP-Password
|
||||||
|
RADIUSAttributeTypeNASIPAddress RADIUSAttributeType = 4 // RFC2865 5.4. NAS-IP-Address
|
||||||
|
RADIUSAttributeTypeNASPort RADIUSAttributeType = 5 // RFC2865 5.5. NAS-Port
|
||||||
|
RADIUSAttributeTypeServiceType RADIUSAttributeType = 6 // RFC2865 5.6. Service-Type
|
||||||
|
RADIUSAttributeTypeFramedProtocol RADIUSAttributeType = 7 // RFC2865 5.7. Framed-Protocol
|
||||||
|
RADIUSAttributeTypeFramedIPAddress RADIUSAttributeType = 8 // RFC2865 5.8. Framed-IP-Address
|
||||||
|
RADIUSAttributeTypeFramedIPNetmask RADIUSAttributeType = 9 // RFC2865 5.9. Framed-IP-Netmask
|
||||||
|
RADIUSAttributeTypeFramedRouting RADIUSAttributeType = 10 // RFC2865 5.10. Framed-Routing
|
||||||
|
RADIUSAttributeTypeFilterId RADIUSAttributeType = 11 // RFC2865 5.11. Filter-Id
|
||||||
|
RADIUSAttributeTypeFramedMTU RADIUSAttributeType = 12 // RFC2865 5.12. Framed-MTU
|
||||||
|
RADIUSAttributeTypeFramedCompression RADIUSAttributeType = 13 // RFC2865 5.13. Framed-Compression
|
||||||
|
RADIUSAttributeTypeLoginIPHost RADIUSAttributeType = 14 // RFC2865 5.14. Login-IP-Host
|
||||||
|
RADIUSAttributeTypeLoginService RADIUSAttributeType = 15 // RFC2865 5.15. Login-Service
|
||||||
|
RADIUSAttributeTypeLoginTCPPort RADIUSAttributeType = 16 // RFC2865 5.16. Login-TCP-Port
|
||||||
|
RADIUSAttributeTypeReplyMessage RADIUSAttributeType = 18 // RFC2865 5.18. Reply-Message
|
||||||
|
RADIUSAttributeTypeCallbackNumber RADIUSAttributeType = 19 // RFC2865 5.19. Callback-Number
|
||||||
|
RADIUSAttributeTypeCallbackId RADIUSAttributeType = 20 // RFC2865 5.20. Callback-Id
|
||||||
|
RADIUSAttributeTypeFramedRoute RADIUSAttributeType = 22 // RFC2865 5.22. Framed-Route
|
||||||
|
RADIUSAttributeTypeFramedIPXNetwork RADIUSAttributeType = 23 // RFC2865 5.23. Framed-IPX-Network
|
||||||
|
RADIUSAttributeTypeState RADIUSAttributeType = 24 // RFC2865 5.24. State
|
||||||
|
RADIUSAttributeTypeClass RADIUSAttributeType = 25 // RFC2865 5.25. Class
|
||||||
|
RADIUSAttributeTypeVendorSpecific RADIUSAttributeType = 26 // RFC2865 5.26. Vendor-Specific
|
||||||
|
RADIUSAttributeTypeSessionTimeout RADIUSAttributeType = 27 // RFC2865 5.27. Session-Timeout
|
||||||
|
RADIUSAttributeTypeIdleTimeout RADIUSAttributeType = 28 // RFC2865 5.28. Idle-Timeout
|
||||||
|
RADIUSAttributeTypeTerminationAction RADIUSAttributeType = 29 // RFC2865 5.29. Termination-Action
|
||||||
|
RADIUSAttributeTypeCalledStationId RADIUSAttributeType = 30 // RFC2865 5.30. Called-Station-Id
|
||||||
|
RADIUSAttributeTypeCallingStationId RADIUSAttributeType = 31 // RFC2865 5.31. Calling-Station-Id
|
||||||
|
RADIUSAttributeTypeNASIdentifier RADIUSAttributeType = 32 // RFC2865 5.32. NAS-Identifier
|
||||||
|
RADIUSAttributeTypeProxyState RADIUSAttributeType = 33 // RFC2865 5.33. Proxy-State
|
||||||
|
RADIUSAttributeTypeLoginLATService RADIUSAttributeType = 34 // RFC2865 5.34. Login-LAT-Service
|
||||||
|
RADIUSAttributeTypeLoginLATNode RADIUSAttributeType = 35 // RFC2865 5.35. Login-LAT-Node
|
||||||
|
RADIUSAttributeTypeLoginLATGroup RADIUSAttributeType = 36 // RFC2865 5.36. Login-LAT-Group
|
||||||
|
RADIUSAttributeTypeFramedAppleTalkLink RADIUSAttributeType = 37 // RFC2865 5.37. Framed-AppleTalk-Link
|
||||||
|
RADIUSAttributeTypeFramedAppleTalkNetwork RADIUSAttributeType = 38 // RFC2865 5.38. Framed-AppleTalk-Network
|
||||||
|
RADIUSAttributeTypeFramedAppleTalkZone RADIUSAttributeType = 39 // RFC2865 5.39. Framed-AppleTalk-Zone
|
||||||
|
RADIUSAttributeTypeAcctStatusType RADIUSAttributeType = 40 // RFC2866 5.1. Acct-Status-Type
|
||||||
|
RADIUSAttributeTypeAcctDelayTime RADIUSAttributeType = 41 // RFC2866 5.2. Acct-Delay-Time
|
||||||
|
RADIUSAttributeTypeAcctInputOctets RADIUSAttributeType = 42 // RFC2866 5.3. Acct-Input-Octets
|
||||||
|
RADIUSAttributeTypeAcctOutputOctets RADIUSAttributeType = 43 // RFC2866 5.4. Acct-Output-Octets
|
||||||
|
RADIUSAttributeTypeAcctSessionId RADIUSAttributeType = 44 // RFC2866 5.5. Acct-Session-Id
|
||||||
|
RADIUSAttributeTypeAcctAuthentic RADIUSAttributeType = 45 // RFC2866 5.6. Acct-Authentic
|
||||||
|
RADIUSAttributeTypeAcctSessionTime RADIUSAttributeType = 46 // RFC2866 5.7. Acct-Session-Time
|
||||||
|
RADIUSAttributeTypeAcctInputPackets RADIUSAttributeType = 47 // RFC2866 5.8. Acct-Input-Packets
|
||||||
|
RADIUSAttributeTypeAcctOutputPackets RADIUSAttributeType = 48 // RFC2866 5.9. Acct-Output-Packets
|
||||||
|
RADIUSAttributeTypeAcctTerminateCause RADIUSAttributeType = 49 // RFC2866 5.10. Acct-Terminate-Cause
|
||||||
|
RADIUSAttributeTypeAcctMultiSessionId RADIUSAttributeType = 50 // RFC2866 5.11. Acct-Multi-Session-Id
|
||||||
|
RADIUSAttributeTypeAcctLinkCount RADIUSAttributeType = 51 // RFC2866 5.12. Acct-Link-Count
|
||||||
|
RADIUSAttributeTypeAcctInputGigawords RADIUSAttributeType = 52 // RFC2869 5.1. Acct-Input-Gigawords
|
||||||
|
RADIUSAttributeTypeAcctOutputGigawords RADIUSAttributeType = 53 // RFC2869 5.2. Acct-Output-Gigawords
|
||||||
|
RADIUSAttributeTypeEventTimestamp RADIUSAttributeType = 55 // RFC2869 5.3. Event-Timestamp
|
||||||
|
RADIUSAttributeTypeCHAPChallenge RADIUSAttributeType = 60 // RFC2865 5.40. CHAP-Challenge
|
||||||
|
RADIUSAttributeTypeNASPortType RADIUSAttributeType = 61 // RFC2865 5.41. NAS-Port-Type
|
||||||
|
RADIUSAttributeTypePortLimit RADIUSAttributeType = 62 // RFC2865 5.42. Port-Limit
|
||||||
|
RADIUSAttributeTypeLoginLATPort RADIUSAttributeType = 63 // RFC2865 5.43. Login-LAT-Port
|
||||||
|
RADIUSAttributeTypeTunnelType RADIUSAttributeType = 64 // RFC2868 3.1. Tunnel-Type
|
||||||
|
RADIUSAttributeTypeTunnelMediumType RADIUSAttributeType = 65 // RFC2868 3.2. Tunnel-Medium-Type
|
||||||
|
RADIUSAttributeTypeTunnelClientEndpoint RADIUSAttributeType = 66 // RFC2868 3.3. Tunnel-Client-Endpoint
|
||||||
|
RADIUSAttributeTypeTunnelServerEndpoint RADIUSAttributeType = 67 // RFC2868 3.4. Tunnel-Server-Endpoint
|
||||||
|
RADIUSAttributeTypeAcctTunnelConnection RADIUSAttributeType = 68 // RFC2867 4.1. Acct-Tunnel-Connection
|
||||||
|
RADIUSAttributeTypeTunnelPassword RADIUSAttributeType = 69 // RFC2868 3.5. Tunnel-Password
|
||||||
|
RADIUSAttributeTypeARAPPassword RADIUSAttributeType = 70 // RFC2869 5.4. ARAP-Password
|
||||||
|
RADIUSAttributeTypeARAPFeatures RADIUSAttributeType = 71 // RFC2869 5.5. ARAP-Features
|
||||||
|
RADIUSAttributeTypeARAPZoneAccess RADIUSAttributeType = 72 // RFC2869 5.6. ARAP-Zone-Access
|
||||||
|
RADIUSAttributeTypeARAPSecurity RADIUSAttributeType = 73 // RFC2869 5.7. ARAP-Security
|
||||||
|
RADIUSAttributeTypeARAPSecurityData RADIUSAttributeType = 74 // RFC2869 5.8. ARAP-Security-Data
|
||||||
|
RADIUSAttributeTypePasswordRetry RADIUSAttributeType = 75 // RFC2869 5.9. Password-Retry
|
||||||
|
RADIUSAttributeTypePrompt RADIUSAttributeType = 76 // RFC2869 5.10. Prompt
|
||||||
|
RADIUSAttributeTypeConnectInfo RADIUSAttributeType = 77 // RFC2869 5.11. Connect-Info
|
||||||
|
RADIUSAttributeTypeConfigurationToken RADIUSAttributeType = 78 // RFC2869 5.12. Configuration-Token
|
||||||
|
RADIUSAttributeTypeEAPMessage RADIUSAttributeType = 79 // RFC2869 5.13. EAP-Message
|
||||||
|
RADIUSAttributeTypeMessageAuthenticator RADIUSAttributeType = 80 // RFC2869 5.14. Message-Authenticator
|
||||||
|
RADIUSAttributeTypeTunnelPrivateGroupID RADIUSAttributeType = 81 // RFC2868 3.6. Tunnel-Private-Group-ID
|
||||||
|
RADIUSAttributeTypeTunnelAssignmentID RADIUSAttributeType = 82 // RFC2868 3.7. Tunnel-Assignment-ID
|
||||||
|
RADIUSAttributeTypeTunnelPreference RADIUSAttributeType = 83 // RFC2868 3.8. Tunnel-Preference
|
||||||
|
RADIUSAttributeTypeARAPChallengeResponse RADIUSAttributeType = 84 // RFC2869 5.15. ARAP-Challenge-Response
|
||||||
|
RADIUSAttributeTypeAcctInterimInterval RADIUSAttributeType = 85 // RFC2869 5.16. Acct-Interim-Interval
|
||||||
|
RADIUSAttributeTypeAcctTunnelPacketsLost RADIUSAttributeType = 86 // RFC2867 4.2. Acct-Tunnel-Packets-Lost
|
||||||
|
RADIUSAttributeTypeNASPortId RADIUSAttributeType = 87 // RFC2869 5.17. NAS-Port-Id
|
||||||
|
RADIUSAttributeTypeFramedPool RADIUSAttributeType = 88 // RFC2869 5.18. Framed-Pool
|
||||||
|
RADIUSAttributeTypeTunnelClientAuthID RADIUSAttributeType = 90 // RFC2868 3.9. Tunnel-Client-Auth-ID
|
||||||
|
RADIUSAttributeTypeTunnelServerAuthID RADIUSAttributeType = 91 // RFC2868 3.10. Tunnel-Server-Auth-ID
|
||||||
|
)
|
||||||
|
|
||||||
|
// RADIUSAttributeType represents attribute length.
|
||||||
|
type RADIUSAttributeLength uint8
|
||||||
|
|
||||||
|
// RADIUSAttributeType represents attribute value.
|
||||||
|
type RADIUSAttributeValue []byte
|
||||||
|
|
||||||
|
// String returns a string version of a RADIUSAttributeType.
|
||||||
|
func (t RADIUSAttributeType) String() (s string) {
|
||||||
|
switch t {
|
||||||
|
case RADIUSAttributeTypeUserName:
|
||||||
|
s = "User-Name"
|
||||||
|
case RADIUSAttributeTypeUserPassword:
|
||||||
|
s = "User-Password"
|
||||||
|
case RADIUSAttributeTypeCHAPPassword:
|
||||||
|
s = "CHAP-Password"
|
||||||
|
case RADIUSAttributeTypeNASIPAddress:
|
||||||
|
s = "NAS-IP-Address"
|
||||||
|
case RADIUSAttributeTypeNASPort:
|
||||||
|
s = "NAS-Port"
|
||||||
|
case RADIUSAttributeTypeServiceType:
|
||||||
|
s = "Service-Type"
|
||||||
|
case RADIUSAttributeTypeFramedProtocol:
|
||||||
|
s = "Framed-Protocol"
|
||||||
|
case RADIUSAttributeTypeFramedIPAddress:
|
||||||
|
s = "Framed-IP-Address"
|
||||||
|
case RADIUSAttributeTypeFramedIPNetmask:
|
||||||
|
s = "Framed-IP-Netmask"
|
||||||
|
case RADIUSAttributeTypeFramedRouting:
|
||||||
|
s = "Framed-Routing"
|
||||||
|
case RADIUSAttributeTypeFilterId:
|
||||||
|
s = "Filter-Id"
|
||||||
|
case RADIUSAttributeTypeFramedMTU:
|
||||||
|
s = "Framed-MTU"
|
||||||
|
case RADIUSAttributeTypeFramedCompression:
|
||||||
|
s = "Framed-Compression"
|
||||||
|
case RADIUSAttributeTypeLoginIPHost:
|
||||||
|
s = "Login-IP-Host"
|
||||||
|
case RADIUSAttributeTypeLoginService:
|
||||||
|
s = "Login-Service"
|
||||||
|
case RADIUSAttributeTypeLoginTCPPort:
|
||||||
|
s = "Login-TCP-Port"
|
||||||
|
case RADIUSAttributeTypeReplyMessage:
|
||||||
|
s = "Reply-Message"
|
||||||
|
case RADIUSAttributeTypeCallbackNumber:
|
||||||
|
s = "Callback-Number"
|
||||||
|
case RADIUSAttributeTypeCallbackId:
|
||||||
|
s = "Callback-Id"
|
||||||
|
case RADIUSAttributeTypeFramedRoute:
|
||||||
|
s = "Framed-Route"
|
||||||
|
case RADIUSAttributeTypeFramedIPXNetwork:
|
||||||
|
s = "Framed-IPX-Network"
|
||||||
|
case RADIUSAttributeTypeState:
|
||||||
|
s = "State"
|
||||||
|
case RADIUSAttributeTypeClass:
|
||||||
|
s = "Class"
|
||||||
|
case RADIUSAttributeTypeVendorSpecific:
|
||||||
|
s = "Vendor-Specific"
|
||||||
|
case RADIUSAttributeTypeSessionTimeout:
|
||||||
|
s = "Session-Timeout"
|
||||||
|
case RADIUSAttributeTypeIdleTimeout:
|
||||||
|
s = "Idle-Timeout"
|
||||||
|
case RADIUSAttributeTypeTerminationAction:
|
||||||
|
s = "Termination-Action"
|
||||||
|
case RADIUSAttributeTypeCalledStationId:
|
||||||
|
s = "Called-Station-Id"
|
||||||
|
case RADIUSAttributeTypeCallingStationId:
|
||||||
|
s = "Calling-Station-Id"
|
||||||
|
case RADIUSAttributeTypeNASIdentifier:
|
||||||
|
s = "NAS-Identifier"
|
||||||
|
case RADIUSAttributeTypeProxyState:
|
||||||
|
s = "Proxy-State"
|
||||||
|
case RADIUSAttributeTypeLoginLATService:
|
||||||
|
s = "Login-LAT-Service"
|
||||||
|
case RADIUSAttributeTypeLoginLATNode:
|
||||||
|
s = "Login-LAT-Node"
|
||||||
|
case RADIUSAttributeTypeLoginLATGroup:
|
||||||
|
s = "Login-LAT-Group"
|
||||||
|
case RADIUSAttributeTypeFramedAppleTalkLink:
|
||||||
|
s = "Framed-AppleTalk-Link"
|
||||||
|
case RADIUSAttributeTypeFramedAppleTalkNetwork:
|
||||||
|
s = "Framed-AppleTalk-Network"
|
||||||
|
case RADIUSAttributeTypeFramedAppleTalkZone:
|
||||||
|
s = "Framed-AppleTalk-Zone"
|
||||||
|
case RADIUSAttributeTypeAcctStatusType:
|
||||||
|
s = "Acct-Status-Type"
|
||||||
|
case RADIUSAttributeTypeAcctDelayTime:
|
||||||
|
s = "Acct-Delay-Time"
|
||||||
|
case RADIUSAttributeTypeAcctInputOctets:
|
||||||
|
s = "Acct-Input-Octets"
|
||||||
|
case RADIUSAttributeTypeAcctOutputOctets:
|
||||||
|
s = "Acct-Output-Octets"
|
||||||
|
case RADIUSAttributeTypeAcctSessionId:
|
||||||
|
s = "Acct-Session-Id"
|
||||||
|
case RADIUSAttributeTypeAcctAuthentic:
|
||||||
|
s = "Acct-Authentic"
|
||||||
|
case RADIUSAttributeTypeAcctSessionTime:
|
||||||
|
s = "Acct-Session-Time"
|
||||||
|
case RADIUSAttributeTypeAcctInputPackets:
|
||||||
|
s = "Acct-Input-Packets"
|
||||||
|
case RADIUSAttributeTypeAcctOutputPackets:
|
||||||
|
s = "Acct-Output-Packets"
|
||||||
|
case RADIUSAttributeTypeAcctTerminateCause:
|
||||||
|
s = "Acct-Terminate-Cause"
|
||||||
|
case RADIUSAttributeTypeAcctMultiSessionId:
|
||||||
|
s = "Acct-Multi-Session-Id"
|
||||||
|
case RADIUSAttributeTypeAcctLinkCount:
|
||||||
|
s = "Acct-Link-Count"
|
||||||
|
case RADIUSAttributeTypeAcctInputGigawords:
|
||||||
|
s = "Acct-Input-Gigawords"
|
||||||
|
case RADIUSAttributeTypeAcctOutputGigawords:
|
||||||
|
s = "Acct-Output-Gigawords"
|
||||||
|
case RADIUSAttributeTypeEventTimestamp:
|
||||||
|
s = "Event-Timestamp"
|
||||||
|
case RADIUSAttributeTypeCHAPChallenge:
|
||||||
|
s = "CHAP-Challenge"
|
||||||
|
case RADIUSAttributeTypeNASPortType:
|
||||||
|
s = "NAS-Port-Type"
|
||||||
|
case RADIUSAttributeTypePortLimit:
|
||||||
|
s = "Port-Limit"
|
||||||
|
case RADIUSAttributeTypeLoginLATPort:
|
||||||
|
s = "Login-LAT-Port"
|
||||||
|
case RADIUSAttributeTypeTunnelType:
|
||||||
|
s = "Tunnel-Type"
|
||||||
|
case RADIUSAttributeTypeTunnelMediumType:
|
||||||
|
s = "Tunnel-Medium-Type"
|
||||||
|
case RADIUSAttributeTypeTunnelClientEndpoint:
|
||||||
|
s = "Tunnel-Client-Endpoint"
|
||||||
|
case RADIUSAttributeTypeTunnelServerEndpoint:
|
||||||
|
s = "Tunnel-Server-Endpoint"
|
||||||
|
case RADIUSAttributeTypeAcctTunnelConnection:
|
||||||
|
s = "Acct-Tunnel-Connection"
|
||||||
|
case RADIUSAttributeTypeTunnelPassword:
|
||||||
|
s = "Tunnel-Password"
|
||||||
|
case RADIUSAttributeTypeARAPPassword:
|
||||||
|
s = "ARAP-Password"
|
||||||
|
case RADIUSAttributeTypeARAPFeatures:
|
||||||
|
s = "ARAP-Features"
|
||||||
|
case RADIUSAttributeTypeARAPZoneAccess:
|
||||||
|
s = "ARAP-Zone-Access"
|
||||||
|
case RADIUSAttributeTypeARAPSecurity:
|
||||||
|
s = "ARAP-Security"
|
||||||
|
case RADIUSAttributeTypeARAPSecurityData:
|
||||||
|
s = "ARAP-Security-Data"
|
||||||
|
case RADIUSAttributeTypePasswordRetry:
|
||||||
|
s = "Password-Retry"
|
||||||
|
case RADIUSAttributeTypePrompt:
|
||||||
|
s = "Prompt"
|
||||||
|
case RADIUSAttributeTypeConnectInfo:
|
||||||
|
s = "Connect-Info"
|
||||||
|
case RADIUSAttributeTypeConfigurationToken:
|
||||||
|
s = "Configuration-Token"
|
||||||
|
case RADIUSAttributeTypeEAPMessage:
|
||||||
|
s = "EAP-Message"
|
||||||
|
case RADIUSAttributeTypeMessageAuthenticator:
|
||||||
|
s = "Message-Authenticator"
|
||||||
|
case RADIUSAttributeTypeTunnelPrivateGroupID:
|
||||||
|
s = "Tunnel-Private-Group-ID"
|
||||||
|
case RADIUSAttributeTypeTunnelAssignmentID:
|
||||||
|
s = "Tunnel-Assignment-ID"
|
||||||
|
case RADIUSAttributeTypeTunnelPreference:
|
||||||
|
s = "Tunnel-Preference"
|
||||||
|
case RADIUSAttributeTypeARAPChallengeResponse:
|
||||||
|
s = "ARAP-Challenge-Response"
|
||||||
|
case RADIUSAttributeTypeAcctInterimInterval:
|
||||||
|
s = "Acct-Interim-Interval"
|
||||||
|
case RADIUSAttributeTypeAcctTunnelPacketsLost:
|
||||||
|
s = "Acct-Tunnel-Packets-Lost"
|
||||||
|
case RADIUSAttributeTypeNASPortId:
|
||||||
|
s = "NAS-Port-Id"
|
||||||
|
case RADIUSAttributeTypeFramedPool:
|
||||||
|
s = "Framed-Pool"
|
||||||
|
case RADIUSAttributeTypeTunnelClientAuthID:
|
||||||
|
s = "Tunnel-Client-Auth-ID"
|
||||||
|
case RADIUSAttributeTypeTunnelServerAuthID:
|
||||||
|
s = "Tunnel-Server-Auth-ID"
|
||||||
|
default:
|
||||||
|
s = fmt.Sprintf("Unknown(%d)", t)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Len returns the length of a RADIUS packet.
|
||||||
|
func (radius *RADIUS) Len() (int, error) {
|
||||||
|
n := radiusMinimumRecordSizeInBytes
|
||||||
|
for _, v := range radius.Attributes {
|
||||||
|
alen, err := attributeValueLength(v.Value)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
n += int(alen) + 2 // Added Type and Length
|
||||||
|
}
|
||||||
|
return n, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// LayerType returns LayerTypeRADIUS.
|
||||||
|
func (radius *RADIUS) LayerType() gopacket.LayerType {
|
||||||
|
return LayerTypeRADIUS
|
||||||
|
}
|
||||||
|
|
||||||
|
// DecodeFromBytes decodes the given bytes into this layer.
|
||||||
|
func (radius *RADIUS) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
|
||||||
|
if len(data) > radiusMaximumRecordSizeInBytes {
|
||||||
|
df.SetTruncated()
|
||||||
|
return fmt.Errorf("RADIUS length %d too big", len(data))
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(data) < radiusMinimumRecordSizeInBytes {
|
||||||
|
df.SetTruncated()
|
||||||
|
return fmt.Errorf("RADIUS length %d too short", len(data))
|
||||||
|
}
|
||||||
|
|
||||||
|
radius.BaseLayer = BaseLayer{Contents: data}
|
||||||
|
|
||||||
|
radius.Code = RADIUSCode(data[0])
|
||||||
|
radius.Identifier = RADIUSIdentifier(data[1])
|
||||||
|
radius.Length = RADIUSLength(binary.BigEndian.Uint16(data[2:4]))
|
||||||
|
|
||||||
|
if int(radius.Length) > radiusMaximumRecordSizeInBytes {
|
||||||
|
df.SetTruncated()
|
||||||
|
return fmt.Errorf("RADIUS length %d too big", radius.Length)
|
||||||
|
}
|
||||||
|
|
||||||
|
if int(radius.Length) < radiusMinimumRecordSizeInBytes {
|
||||||
|
df.SetTruncated()
|
||||||
|
return fmt.Errorf("RADIUS length %d too short", radius.Length)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RFC 2865 3. Packet Format
|
||||||
|
// `If the packet is shorter than the Length field indicates, it MUST be silently discarded.`
|
||||||
|
if int(radius.Length) > len(data) {
|
||||||
|
df.SetTruncated()
|
||||||
|
return fmt.Errorf("RADIUS length %d too big", radius.Length)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RFC 2865 3. Packet Format
|
||||||
|
// `Octets outside the range of the Length field MUST be treated as padding and ignored on reception.`
|
||||||
|
if int(radius.Length) < len(data) {
|
||||||
|
df.SetTruncated()
|
||||||
|
data = data[:radius.Length]
|
||||||
|
}
|
||||||
|
|
||||||
|
copy(radius.Authenticator[:], data[4:20])
|
||||||
|
|
||||||
|
if len(data) == radiusMinimumRecordSizeInBytes {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
pos := radiusMinimumRecordSizeInBytes
|
||||||
|
for {
|
||||||
|
if len(data) == pos {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(data[pos:]) < radiusAttributesMinimumRecordSizeInBytes {
|
||||||
|
df.SetTruncated()
|
||||||
|
return fmt.Errorf("RADIUS attributes length %d too short", len(data[pos:]))
|
||||||
|
}
|
||||||
|
|
||||||
|
attr := RADIUSAttribute{}
|
||||||
|
attr.Type = RADIUSAttributeType(data[pos])
|
||||||
|
attr.Length = RADIUSAttributeLength(data[pos+1])
|
||||||
|
|
||||||
|
if int(attr.Length) > len(data[pos:]) {
|
||||||
|
df.SetTruncated()
|
||||||
|
return fmt.Errorf("RADIUS attributes length %d too big", attr.Length)
|
||||||
|
}
|
||||||
|
|
||||||
|
if int(attr.Length) < radiusAttributesMinimumRecordSizeInBytes {
|
||||||
|
df.SetTruncated()
|
||||||
|
return fmt.Errorf("RADIUS attributes length %d too short", attr.Length)
|
||||||
|
}
|
||||||
|
|
||||||
|
if int(attr.Length) > radiusAttributesMinimumRecordSizeInBytes {
|
||||||
|
attr.Value = make([]byte, attr.Length-2)
|
||||||
|
copy(attr.Value[:], data[pos+2:pos+int(attr.Length)])
|
||||||
|
radius.Attributes = append(radius.Attributes, attr)
|
||||||
|
}
|
||||||
|
|
||||||
|
pos += int(attr.Length)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, v := range radius.Attributes {
|
||||||
|
if v.Type == RADIUSAttributeTypeEAPMessage {
|
||||||
|
radius.BaseLayer.Payload = append(radius.BaseLayer.Payload, v.Value...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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 (radius *RADIUS) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
|
||||||
|
plen, err := radius.Len()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if opts.FixLengths {
|
||||||
|
radius.Length = RADIUSLength(plen)
|
||||||
|
}
|
||||||
|
|
||||||
|
data, err := b.PrependBytes(plen)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
data[0] = byte(radius.Code)
|
||||||
|
data[1] = byte(radius.Identifier)
|
||||||
|
binary.BigEndian.PutUint16(data[2:], uint16(radius.Length))
|
||||||
|
copy(data[4:20], radius.Authenticator[:])
|
||||||
|
|
||||||
|
pos := radiusMinimumRecordSizeInBytes
|
||||||
|
for _, v := range radius.Attributes {
|
||||||
|
if opts.FixLengths {
|
||||||
|
v.Length, err = attributeValueLength(v.Value)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
data[pos] = byte(v.Type)
|
||||||
|
data[pos+1] = byte(v.Length)
|
||||||
|
copy(data[pos+2:], v.Value[:])
|
||||||
|
|
||||||
|
pos += len(v.Value) + 2 // Added Type and Length
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CanDecode returns the set of layer types that this DecodingLayer can decode.
|
||||||
|
func (radius *RADIUS) CanDecode() gopacket.LayerClass {
|
||||||
|
return LayerTypeRADIUS
|
||||||
|
}
|
||||||
|
|
||||||
|
// NextLayerType returns the layer type contained by this DecodingLayer.
|
||||||
|
func (radius *RADIUS) NextLayerType() gopacket.LayerType {
|
||||||
|
if len(radius.BaseLayer.Payload) > 0 {
|
||||||
|
return LayerTypeEAP
|
||||||
|
} else {
|
||||||
|
return gopacket.LayerTypeZero
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Payload returns the EAP Type-Data for EAP-Message attributes.
|
||||||
|
func (radius *RADIUS) Payload() []byte {
|
||||||
|
return radius.BaseLayer.Payload
|
||||||
|
}
|
||||||
|
|
||||||
|
func decodeRADIUS(data []byte, p gopacket.PacketBuilder) error {
|
||||||
|
radius := &RADIUS{}
|
||||||
|
err := radius.DecodeFromBytes(data, p)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
p.AddLayer(radius)
|
||||||
|
p.SetApplicationLayer(radius)
|
||||||
|
next := radius.NextLayerType()
|
||||||
|
if next == gopacket.LayerTypeZero {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return p.NextDecoder(next)
|
||||||
|
}
|
||||||
|
|
||||||
|
func attributeValueLength(v []byte) (RADIUSAttributeLength, error) {
|
||||||
|
n := len(v)
|
||||||
|
if n > 255 {
|
||||||
|
return 0, fmt.Errorf("RADIUS attribute value length %d too long", n)
|
||||||
|
} else {
|
||||||
|
return RADIUSAttributeLength(n), nil
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,170 @@
|
||||||
|
// Copyright 2019 The GoPacket Authors. All rights reserved.
|
||||||
|
//
|
||||||
|
// Use of this source code is governed by a BSD-style license that can be found
|
||||||
|
// in the LICENSE file in the root of the source tree.
|
||||||
|
|
||||||
|
package layers
|
||||||
|
|
||||||
|
// This file implements the ASF-RMCP header specified in section 3.2.2.2 of
|
||||||
|
// https://www.dmtf.org/sites/default/files/standards/documents/DSP0136.pdf
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/google/gopacket"
|
||||||
|
)
|
||||||
|
|
||||||
|
// RMCPClass is the class of a RMCP layer's payload, e.g. ASF or IPMI. This is a
|
||||||
|
// 4-bit unsigned int on the wire; all but 6 (ASF), 7 (IPMI) and 8 (OEM-defined)
|
||||||
|
// are currently reserved.
|
||||||
|
type RMCPClass uint8
|
||||||
|
|
||||||
|
// LayerType returns the payload layer type corresponding to a RMCP class.
|
||||||
|
func (c RMCPClass) LayerType() gopacket.LayerType {
|
||||||
|
if lt := rmcpClassLayerTypes[uint8(c)]; lt != 0 {
|
||||||
|
return lt
|
||||||
|
}
|
||||||
|
return gopacket.LayerTypePayload
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c RMCPClass) String() string {
|
||||||
|
return fmt.Sprintf("%v(%v)", uint8(c), c.LayerType())
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
// RMCPVersion1 identifies RMCP v1.0 in the Version header field. Lower
|
||||||
|
// values are considered legacy, while higher values are reserved by the
|
||||||
|
// specification.
|
||||||
|
RMCPVersion1 uint8 = 0x06
|
||||||
|
|
||||||
|
// RMCPNormal indicates a "normal" message, i.e. not an acknowledgement.
|
||||||
|
RMCPNormal uint8 = 0
|
||||||
|
|
||||||
|
// RMCPAck indicates a message is acknowledging a received normal message.
|
||||||
|
RMCPAck uint8 = 1 << 7
|
||||||
|
|
||||||
|
// RMCPClassASF identifies an RMCP message as containing an ASF-RMCP
|
||||||
|
// payload.
|
||||||
|
RMCPClassASF RMCPClass = 0x06
|
||||||
|
|
||||||
|
// RMCPClassIPMI identifies an RMCP message as containing an IPMI payload.
|
||||||
|
RMCPClassIPMI RMCPClass = 0x07
|
||||||
|
|
||||||
|
// RMCPClassOEM identifies an RMCP message as containing an OEM-defined
|
||||||
|
// payload.
|
||||||
|
RMCPClassOEM RMCPClass = 0x08
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
rmcpClassLayerTypes = [16]gopacket.LayerType{
|
||||||
|
RMCPClassASF: LayerTypeASF,
|
||||||
|
// RMCPClassIPMI is to implement; RMCPClassOEM is deliberately not
|
||||||
|
// implemented, so we return LayerTypePayload
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// RegisterRMCPLayerType allows specifying that the payload of a RMCP packet of
|
||||||
|
// a certain class should processed by the provided layer type. This overrides
|
||||||
|
// any existing registrations, including defaults.
|
||||||
|
func RegisterRMCPLayerType(c RMCPClass, l gopacket.LayerType) {
|
||||||
|
rmcpClassLayerTypes[c] = l
|
||||||
|
}
|
||||||
|
|
||||||
|
// RMCP describes the format of an RMCP header, which forms a UDP payload. See
|
||||||
|
// section 3.2.2.2.
|
||||||
|
type RMCP struct {
|
||||||
|
BaseLayer
|
||||||
|
|
||||||
|
// Version identifies the version of the RMCP header. 0x06 indicates RMCP
|
||||||
|
// v1.0; lower values are legacy, higher values are reserved.
|
||||||
|
Version uint8
|
||||||
|
|
||||||
|
// Sequence is the sequence number assicated with the message. Note that
|
||||||
|
// this rolls over to 0 after 254, not 255. Seq num 255 indicates the
|
||||||
|
// receiver must not send an ACK.
|
||||||
|
Sequence uint8
|
||||||
|
|
||||||
|
// Ack indicates whether this packet is an acknowledgement. If it is, the
|
||||||
|
// payload will be empty.
|
||||||
|
Ack bool
|
||||||
|
|
||||||
|
// Class idicates the structure of the payload. There are only 2^4 valid
|
||||||
|
// values, however there is no uint4 data type. N.B. the Ack bit has been
|
||||||
|
// split off into another field. The most significant 4 bits of this field
|
||||||
|
// will always be 0.
|
||||||
|
Class RMCPClass
|
||||||
|
}
|
||||||
|
|
||||||
|
// LayerType returns LayerTypeRMCP. It partially satisfies Layer and
|
||||||
|
// SerializableLayer.
|
||||||
|
func (*RMCP) LayerType() gopacket.LayerType {
|
||||||
|
return LayerTypeRMCP
|
||||||
|
}
|
||||||
|
|
||||||
|
// CanDecode returns LayerTypeRMCP. It partially satisfies DecodingLayer.
|
||||||
|
func (r *RMCP) CanDecode() gopacket.LayerClass {
|
||||||
|
return r.LayerType()
|
||||||
|
}
|
||||||
|
|
||||||
|
// DecodeFromBytes makes the layer represent the provided bytes. It partially
|
||||||
|
// satisfies DecodingLayer.
|
||||||
|
func (r *RMCP) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
|
||||||
|
if len(data) < 4 {
|
||||||
|
df.SetTruncated()
|
||||||
|
return fmt.Errorf("invalid RMCP header, length %v less than 4",
|
||||||
|
len(data))
|
||||||
|
}
|
||||||
|
|
||||||
|
r.BaseLayer.Contents = data[:4]
|
||||||
|
r.BaseLayer.Payload = data[4:]
|
||||||
|
|
||||||
|
r.Version = uint8(data[0])
|
||||||
|
// 1 byte reserved
|
||||||
|
r.Sequence = uint8(data[2])
|
||||||
|
r.Ack = data[3]&RMCPAck != 0
|
||||||
|
r.Class = RMCPClass(data[3] & 0xF)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NextLayerType returns the data layer of this RMCP layer. This partially
|
||||||
|
// satisfies DecodingLayer.
|
||||||
|
func (r *RMCP) NextLayerType() gopacket.LayerType {
|
||||||
|
return r.Class.LayerType()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Payload returns the data layer. It partially satisfies ApplicationLayer.
|
||||||
|
func (r *RMCP) Payload() []byte {
|
||||||
|
return r.BaseLayer.Payload
|
||||||
|
}
|
||||||
|
|
||||||
|
// SerializeTo writes the serialized fom of this layer into the SerializeBuffer,
|
||||||
|
// partially satisfying SerializableLayer.
|
||||||
|
func (r *RMCP) SerializeTo(b gopacket.SerializeBuffer, _ gopacket.SerializeOptions) error {
|
||||||
|
// The IPMI v1.5 spec contains a pad byte for frame sizes of certain lengths
|
||||||
|
// to work around issues in LAN chips. This is no longer necessary as of
|
||||||
|
// IPMI v2.0 (renamed to "legacy pad") so we do not attempt to add it. The
|
||||||
|
// same approach is taken by FreeIPMI:
|
||||||
|
// http://git.savannah.gnu.org/cgit/freeipmi.git/tree/libfreeipmi/interface/ipmi-lan-interface.c?id=b5ffcd38317daf42074458879f4c55ba6804a595#n836
|
||||||
|
bytes, err := b.PrependBytes(4)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
bytes[0] = r.Version
|
||||||
|
bytes[1] = 0x00
|
||||||
|
bytes[2] = r.Sequence
|
||||||
|
bytes[3] = bool2uint8(r.Ack)<<7 | uint8(r.Class) // thanks, BFD layer
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// decodeRMCP decodes the byte slice into an RMCP type, and sets the application
|
||||||
|
// layer to it.
|
||||||
|
func decodeRMCP(data []byte, p gopacket.PacketBuilder) error {
|
||||||
|
rmcp := &RMCP{}
|
||||||
|
err := rmcp.DecodeFromBytes(data, p)
|
||||||
|
p.AddLayer(rmcp)
|
||||||
|
p.SetApplicationLayer(rmcp)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return p.NextDecoder(rmcp.NextLayerType())
|
||||||
|
}
|
|
@ -0,0 +1,93 @@
|
||||||
|
// 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 RUDP struct {
|
||||||
|
BaseLayer
|
||||||
|
SYN, ACK, EACK, RST, NUL bool
|
||||||
|
Version uint8
|
||||||
|
HeaderLength uint8
|
||||||
|
SrcPort, DstPort RUDPPort
|
||||||
|
DataLength uint16
|
||||||
|
Seq, Ack, Checksum uint32
|
||||||
|
VariableHeaderArea []byte
|
||||||
|
// RUDPHeaderSyn contains SYN information for the RUDP packet,
|
||||||
|
// if the SYN flag is set
|
||||||
|
*RUDPHeaderSYN
|
||||||
|
// RUDPHeaderEack contains EACK information for the RUDP packet,
|
||||||
|
// if the EACK flag is set.
|
||||||
|
*RUDPHeaderEACK
|
||||||
|
}
|
||||||
|
|
||||||
|
type RUDPHeaderSYN struct {
|
||||||
|
MaxOutstandingSegments, MaxSegmentSize, OptionFlags uint16
|
||||||
|
}
|
||||||
|
|
||||||
|
type RUDPHeaderEACK struct {
|
||||||
|
SeqsReceivedOK []uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
// LayerType returns gopacket.LayerTypeRUDP.
|
||||||
|
func (r *RUDP) LayerType() gopacket.LayerType { return LayerTypeRUDP }
|
||||||
|
|
||||||
|
func decodeRUDP(data []byte, p gopacket.PacketBuilder) error {
|
||||||
|
r := &RUDP{
|
||||||
|
SYN: data[0]&0x80 != 0,
|
||||||
|
ACK: data[0]&0x40 != 0,
|
||||||
|
EACK: data[0]&0x20 != 0,
|
||||||
|
RST: data[0]&0x10 != 0,
|
||||||
|
NUL: data[0]&0x08 != 0,
|
||||||
|
Version: data[0] & 0x3,
|
||||||
|
HeaderLength: data[1],
|
||||||
|
SrcPort: RUDPPort(data[2]),
|
||||||
|
DstPort: RUDPPort(data[3]),
|
||||||
|
DataLength: binary.BigEndian.Uint16(data[4:6]),
|
||||||
|
Seq: binary.BigEndian.Uint32(data[6:10]),
|
||||||
|
Ack: binary.BigEndian.Uint32(data[10:14]),
|
||||||
|
Checksum: binary.BigEndian.Uint32(data[14:18]),
|
||||||
|
}
|
||||||
|
if r.HeaderLength < 9 {
|
||||||
|
return fmt.Errorf("RUDP packet with too-short header length %d", r.HeaderLength)
|
||||||
|
}
|
||||||
|
hlen := int(r.HeaderLength) * 2
|
||||||
|
r.Contents = data[:hlen]
|
||||||
|
r.Payload = data[hlen : hlen+int(r.DataLength)]
|
||||||
|
r.VariableHeaderArea = data[18:hlen]
|
||||||
|
headerData := r.VariableHeaderArea
|
||||||
|
switch {
|
||||||
|
case r.SYN:
|
||||||
|
if len(headerData) != 6 {
|
||||||
|
return fmt.Errorf("RUDP packet invalid SYN header length: %d", len(headerData))
|
||||||
|
}
|
||||||
|
r.RUDPHeaderSYN = &RUDPHeaderSYN{
|
||||||
|
MaxOutstandingSegments: binary.BigEndian.Uint16(headerData[:2]),
|
||||||
|
MaxSegmentSize: binary.BigEndian.Uint16(headerData[2:4]),
|
||||||
|
OptionFlags: binary.BigEndian.Uint16(headerData[4:6]),
|
||||||
|
}
|
||||||
|
case r.EACK:
|
||||||
|
if len(headerData)%4 != 0 {
|
||||||
|
return fmt.Errorf("RUDP packet invalid EACK header length: %d", len(headerData))
|
||||||
|
}
|
||||||
|
r.RUDPHeaderEACK = &RUDPHeaderEACK{make([]uint32, len(headerData)/4)}
|
||||||
|
for i := 0; i < len(headerData); i += 4 {
|
||||||
|
r.SeqsReceivedOK[i/4] = binary.BigEndian.Uint32(headerData[i : i+4])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
p.AddLayer(r)
|
||||||
|
p.SetTransportLayer(r)
|
||||||
|
return p.NextDecoder(gopacket.LayerTypePayload)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *RUDP) TransportFlow() gopacket.Flow {
|
||||||
|
return gopacket.NewFlow(EndpointRUDPPort, []byte{byte(r.SrcPort)}, []byte{byte(r.DstPort)})
|
||||||
|
}
|
|
@ -0,0 +1,746 @@
|
||||||
|
// 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"
|
||||||
|
"fmt"
|
||||||
|
"hash/crc32"
|
||||||
|
|
||||||
|
"github.com/google/gopacket"
|
||||||
|
)
|
||||||
|
|
||||||
|
// SCTP contains information on the top level of an SCTP packet.
|
||||||
|
type SCTP struct {
|
||||||
|
BaseLayer
|
||||||
|
SrcPort, DstPort SCTPPort
|
||||||
|
VerificationTag uint32
|
||||||
|
Checksum uint32
|
||||||
|
sPort, dPort []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
// LayerType returns gopacket.LayerTypeSCTP
|
||||||
|
func (s *SCTP) LayerType() gopacket.LayerType { return LayerTypeSCTP }
|
||||||
|
|
||||||
|
func decodeSCTP(data []byte, p gopacket.PacketBuilder) error {
|
||||||
|
sctp := &SCTP{}
|
||||||
|
err := sctp.DecodeFromBytes(data, p)
|
||||||
|
p.AddLayer(sctp)
|
||||||
|
p.SetTransportLayer(sctp)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return p.NextDecoder(sctpChunkTypePrefixDecoder)
|
||||||
|
}
|
||||||
|
|
||||||
|
var sctpChunkTypePrefixDecoder = gopacket.DecodeFunc(decodeWithSCTPChunkTypePrefix)
|
||||||
|
|
||||||
|
// TransportFlow returns a flow based on the source and destination SCTP port.
|
||||||
|
func (s *SCTP) TransportFlow() gopacket.Flow {
|
||||||
|
return gopacket.NewFlow(EndpointSCTPPort, s.sPort, s.dPort)
|
||||||
|
}
|
||||||
|
|
||||||
|
func decodeWithSCTPChunkTypePrefix(data []byte, p gopacket.PacketBuilder) error {
|
||||||
|
chunkType := SCTPChunkType(data[0])
|
||||||
|
return chunkType.Decode(data, p)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SerializeTo is for gopacket.SerializableLayer.
|
||||||
|
func (s SCTP) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
|
||||||
|
bytes, err := b.PrependBytes(12)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
binary.BigEndian.PutUint16(bytes[0:2], uint16(s.SrcPort))
|
||||||
|
binary.BigEndian.PutUint16(bytes[2:4], uint16(s.DstPort))
|
||||||
|
binary.BigEndian.PutUint32(bytes[4:8], s.VerificationTag)
|
||||||
|
if opts.ComputeChecksums {
|
||||||
|
// Note: MakeTable(Castagnoli) actually only creates the table once, then
|
||||||
|
// passes back a singleton on every other call, so this shouldn't cause
|
||||||
|
// excessive memory allocation.
|
||||||
|
binary.LittleEndian.PutUint32(bytes[8:12], crc32.Checksum(b.Bytes(), crc32.MakeTable(crc32.Castagnoli)))
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sctp *SCTP) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
|
||||||
|
if len(data) < 12 {
|
||||||
|
return errors.New("Invalid SCTP common header length")
|
||||||
|
}
|
||||||
|
sctp.SrcPort = SCTPPort(binary.BigEndian.Uint16(data[:2]))
|
||||||
|
sctp.sPort = data[:2]
|
||||||
|
sctp.DstPort = SCTPPort(binary.BigEndian.Uint16(data[2:4]))
|
||||||
|
sctp.dPort = data[2:4]
|
||||||
|
sctp.VerificationTag = binary.BigEndian.Uint32(data[4:8])
|
||||||
|
sctp.Checksum = binary.BigEndian.Uint32(data[8:12])
|
||||||
|
sctp.BaseLayer = BaseLayer{data[:12], data[12:]}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *SCTP) CanDecode() gopacket.LayerClass {
|
||||||
|
return LayerTypeSCTP
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *SCTP) NextLayerType() gopacket.LayerType {
|
||||||
|
return gopacket.LayerTypePayload
|
||||||
|
}
|
||||||
|
|
||||||
|
// SCTPChunk contains the common fields in all SCTP chunks.
|
||||||
|
type SCTPChunk struct {
|
||||||
|
BaseLayer
|
||||||
|
Type SCTPChunkType
|
||||||
|
Flags uint8
|
||||||
|
Length uint16
|
||||||
|
// ActualLength is the total length of an SCTP chunk, including padding.
|
||||||
|
// SCTP chunks start and end on 4-byte boundaries. So if a chunk has a length
|
||||||
|
// of 18, it means that it has data up to and including byte 18, then padding
|
||||||
|
// up to the next 4-byte boundary, 20. In this case, Length would be 18, and
|
||||||
|
// ActualLength would be 20.
|
||||||
|
ActualLength int
|
||||||
|
}
|
||||||
|
|
||||||
|
func roundUpToNearest4(i int) int {
|
||||||
|
if i%4 == 0 {
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
return i + 4 - (i % 4)
|
||||||
|
}
|
||||||
|
|
||||||
|
func decodeSCTPChunk(data []byte) (SCTPChunk, error) {
|
||||||
|
length := binary.BigEndian.Uint16(data[2:4])
|
||||||
|
if length < 4 {
|
||||||
|
return SCTPChunk{}, errors.New("invalid SCTP chunk length")
|
||||||
|
}
|
||||||
|
actual := roundUpToNearest4(int(length))
|
||||||
|
ct := SCTPChunkType(data[0])
|
||||||
|
|
||||||
|
// For SCTP Data, use a separate layer for the payload
|
||||||
|
delta := 0
|
||||||
|
if ct == SCTPChunkTypeData {
|
||||||
|
delta = int(actual) - int(length)
|
||||||
|
actual = 16
|
||||||
|
}
|
||||||
|
|
||||||
|
return SCTPChunk{
|
||||||
|
Type: ct,
|
||||||
|
Flags: data[1],
|
||||||
|
Length: length,
|
||||||
|
ActualLength: actual,
|
||||||
|
BaseLayer: BaseLayer{data[:actual], data[actual : len(data)-delta]},
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SCTPParameter is a TLV parameter inside a SCTPChunk.
|
||||||
|
type SCTPParameter struct {
|
||||||
|
Type uint16
|
||||||
|
Length uint16
|
||||||
|
ActualLength int
|
||||||
|
Value []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
func decodeSCTPParameter(data []byte) SCTPParameter {
|
||||||
|
length := binary.BigEndian.Uint16(data[2:4])
|
||||||
|
return SCTPParameter{
|
||||||
|
Type: binary.BigEndian.Uint16(data[0:2]),
|
||||||
|
Length: length,
|
||||||
|
Value: data[4:length],
|
||||||
|
ActualLength: roundUpToNearest4(int(length)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p SCTPParameter) Bytes() []byte {
|
||||||
|
length := 4 + len(p.Value)
|
||||||
|
data := make([]byte, roundUpToNearest4(length))
|
||||||
|
binary.BigEndian.PutUint16(data[0:2], p.Type)
|
||||||
|
binary.BigEndian.PutUint16(data[2:4], uint16(length))
|
||||||
|
copy(data[4:], p.Value)
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
|
||||||
|
// SCTPUnknownChunkType is the layer type returned when we don't recognize the
|
||||||
|
// chunk type. Since there's a length in a known location, we can skip over
|
||||||
|
// it even if we don't know what it is, and continue parsing the rest of the
|
||||||
|
// chunks. This chunk is stored as an ErrorLayer in the packet.
|
||||||
|
type SCTPUnknownChunkType struct {
|
||||||
|
SCTPChunk
|
||||||
|
bytes []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
func decodeSCTPChunkTypeUnknown(data []byte, p gopacket.PacketBuilder) error {
|
||||||
|
chunk, err := decodeSCTPChunk(data)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
sc := &SCTPUnknownChunkType{SCTPChunk: chunk}
|
||||||
|
sc.bytes = data[:sc.ActualLength]
|
||||||
|
p.AddLayer(sc)
|
||||||
|
p.SetErrorLayer(sc)
|
||||||
|
return p.NextDecoder(gopacket.DecodeFunc(decodeWithSCTPChunkTypePrefix))
|
||||||
|
}
|
||||||
|
|
||||||
|
// SerializeTo is for gopacket.SerializableLayer.
|
||||||
|
func (s SCTPUnknownChunkType) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
|
||||||
|
bytes, err := b.PrependBytes(s.ActualLength)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
copy(bytes, s.bytes)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// LayerType returns gopacket.LayerTypeSCTPUnknownChunkType.
|
||||||
|
func (s *SCTPUnknownChunkType) LayerType() gopacket.LayerType { return LayerTypeSCTPUnknownChunkType }
|
||||||
|
|
||||||
|
// Payload returns all bytes in this header, including the decoded Type, Length,
|
||||||
|
// and Flags.
|
||||||
|
func (s *SCTPUnknownChunkType) Payload() []byte { return s.bytes }
|
||||||
|
|
||||||
|
// Error implements ErrorLayer.
|
||||||
|
func (s *SCTPUnknownChunkType) Error() error {
|
||||||
|
return fmt.Errorf("No decode method available for SCTP chunk type %s", s.Type)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SCTPData is the SCTP Data chunk layer.
|
||||||
|
type SCTPData struct {
|
||||||
|
SCTPChunk
|
||||||
|
Unordered, BeginFragment, EndFragment bool
|
||||||
|
TSN uint32
|
||||||
|
StreamId uint16
|
||||||
|
StreamSequence uint16
|
||||||
|
PayloadProtocol SCTPPayloadProtocol
|
||||||
|
}
|
||||||
|
|
||||||
|
// LayerType returns gopacket.LayerTypeSCTPData.
|
||||||
|
func (s *SCTPData) LayerType() gopacket.LayerType { return LayerTypeSCTPData }
|
||||||
|
|
||||||
|
// SCTPPayloadProtocol represents a payload protocol
|
||||||
|
type SCTPPayloadProtocol uint32
|
||||||
|
|
||||||
|
// SCTPPayloadProtocol constonts from http://www.iana.org/assignments/sctp-parameters/sctp-parameters.xhtml
|
||||||
|
const (
|
||||||
|
SCTPProtocolReserved SCTPPayloadProtocol = 0
|
||||||
|
SCTPPayloadUIA = 1
|
||||||
|
SCTPPayloadM2UA = 2
|
||||||
|
SCTPPayloadM3UA = 3
|
||||||
|
SCTPPayloadSUA = 4
|
||||||
|
SCTPPayloadM2PA = 5
|
||||||
|
SCTPPayloadV5UA = 6
|
||||||
|
SCTPPayloadH248 = 7
|
||||||
|
SCTPPayloadBICC = 8
|
||||||
|
SCTPPayloadTALI = 9
|
||||||
|
SCTPPayloadDUA = 10
|
||||||
|
SCTPPayloadASAP = 11
|
||||||
|
SCTPPayloadENRP = 12
|
||||||
|
SCTPPayloadH323 = 13
|
||||||
|
SCTPPayloadQIPC = 14
|
||||||
|
SCTPPayloadSIMCO = 15
|
||||||
|
SCTPPayloadDDPSegment = 16
|
||||||
|
SCTPPayloadDDPStream = 17
|
||||||
|
SCTPPayloadS1AP = 18
|
||||||
|
)
|
||||||
|
|
||||||
|
func (p SCTPPayloadProtocol) String() string {
|
||||||
|
switch p {
|
||||||
|
case SCTPProtocolReserved:
|
||||||
|
return "Reserved"
|
||||||
|
case SCTPPayloadUIA:
|
||||||
|
return "UIA"
|
||||||
|
case SCTPPayloadM2UA:
|
||||||
|
return "M2UA"
|
||||||
|
case SCTPPayloadM3UA:
|
||||||
|
return "M3UA"
|
||||||
|
case SCTPPayloadSUA:
|
||||||
|
return "SUA"
|
||||||
|
case SCTPPayloadM2PA:
|
||||||
|
return "M2PA"
|
||||||
|
case SCTPPayloadV5UA:
|
||||||
|
return "V5UA"
|
||||||
|
case SCTPPayloadH248:
|
||||||
|
return "H.248"
|
||||||
|
case SCTPPayloadBICC:
|
||||||
|
return "BICC"
|
||||||
|
case SCTPPayloadTALI:
|
||||||
|
return "TALI"
|
||||||
|
case SCTPPayloadDUA:
|
||||||
|
return "DUA"
|
||||||
|
case SCTPPayloadASAP:
|
||||||
|
return "ASAP"
|
||||||
|
case SCTPPayloadENRP:
|
||||||
|
return "ENRP"
|
||||||
|
case SCTPPayloadH323:
|
||||||
|
return "H.323"
|
||||||
|
case SCTPPayloadQIPC:
|
||||||
|
return "QIPC"
|
||||||
|
case SCTPPayloadSIMCO:
|
||||||
|
return "SIMCO"
|
||||||
|
case SCTPPayloadDDPSegment:
|
||||||
|
return "DDPSegment"
|
||||||
|
case SCTPPayloadDDPStream:
|
||||||
|
return "DDPStream"
|
||||||
|
case SCTPPayloadS1AP:
|
||||||
|
return "S1AP"
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("Unknown(%d)", p)
|
||||||
|
}
|
||||||
|
|
||||||
|
func decodeSCTPData(data []byte, p gopacket.PacketBuilder) error {
|
||||||
|
chunk, err := decodeSCTPChunk(data)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
sc := &SCTPData{
|
||||||
|
SCTPChunk: chunk,
|
||||||
|
Unordered: data[1]&0x4 != 0,
|
||||||
|
BeginFragment: data[1]&0x2 != 0,
|
||||||
|
EndFragment: data[1]&0x1 != 0,
|
||||||
|
TSN: binary.BigEndian.Uint32(data[4:8]),
|
||||||
|
StreamId: binary.BigEndian.Uint16(data[8:10]),
|
||||||
|
StreamSequence: binary.BigEndian.Uint16(data[10:12]),
|
||||||
|
PayloadProtocol: SCTPPayloadProtocol(binary.BigEndian.Uint32(data[12:16])),
|
||||||
|
}
|
||||||
|
// Length is the length in bytes of the data, INCLUDING the 16-byte header.
|
||||||
|
p.AddLayer(sc)
|
||||||
|
return p.NextDecoder(gopacket.LayerTypePayload)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SerializeTo is for gopacket.SerializableLayer.
|
||||||
|
func (sc SCTPData) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
|
||||||
|
payload := b.Bytes()
|
||||||
|
// Pad the payload to a 32 bit boundary
|
||||||
|
if rem := len(payload) % 4; rem != 0 {
|
||||||
|
b.AppendBytes(4 - rem)
|
||||||
|
}
|
||||||
|
length := 16
|
||||||
|
bytes, err := b.PrependBytes(length)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
bytes[0] = uint8(sc.Type)
|
||||||
|
flags := uint8(0)
|
||||||
|
if sc.Unordered {
|
||||||
|
flags |= 0x4
|
||||||
|
}
|
||||||
|
if sc.BeginFragment {
|
||||||
|
flags |= 0x2
|
||||||
|
}
|
||||||
|
if sc.EndFragment {
|
||||||
|
flags |= 0x1
|
||||||
|
}
|
||||||
|
bytes[1] = flags
|
||||||
|
binary.BigEndian.PutUint16(bytes[2:4], uint16(length+len(payload)))
|
||||||
|
binary.BigEndian.PutUint32(bytes[4:8], sc.TSN)
|
||||||
|
binary.BigEndian.PutUint16(bytes[8:10], sc.StreamId)
|
||||||
|
binary.BigEndian.PutUint16(bytes[10:12], sc.StreamSequence)
|
||||||
|
binary.BigEndian.PutUint32(bytes[12:16], uint32(sc.PayloadProtocol))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SCTPInitParameter is a parameter for an SCTP Init or InitAck packet.
|
||||||
|
type SCTPInitParameter SCTPParameter
|
||||||
|
|
||||||
|
// SCTPInit is used as the return value for both SCTPInit and SCTPInitAck
|
||||||
|
// messages.
|
||||||
|
type SCTPInit struct {
|
||||||
|
SCTPChunk
|
||||||
|
InitiateTag uint32
|
||||||
|
AdvertisedReceiverWindowCredit uint32
|
||||||
|
OutboundStreams, InboundStreams uint16
|
||||||
|
InitialTSN uint32
|
||||||
|
Parameters []SCTPInitParameter
|
||||||
|
}
|
||||||
|
|
||||||
|
// LayerType returns either gopacket.LayerTypeSCTPInit or gopacket.LayerTypeSCTPInitAck.
|
||||||
|
func (sc *SCTPInit) LayerType() gopacket.LayerType {
|
||||||
|
if sc.Type == SCTPChunkTypeInitAck {
|
||||||
|
return LayerTypeSCTPInitAck
|
||||||
|
}
|
||||||
|
// sc.Type == SCTPChunkTypeInit
|
||||||
|
return LayerTypeSCTPInit
|
||||||
|
}
|
||||||
|
|
||||||
|
func decodeSCTPInit(data []byte, p gopacket.PacketBuilder) error {
|
||||||
|
chunk, err := decodeSCTPChunk(data)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
sc := &SCTPInit{
|
||||||
|
SCTPChunk: chunk,
|
||||||
|
InitiateTag: binary.BigEndian.Uint32(data[4:8]),
|
||||||
|
AdvertisedReceiverWindowCredit: binary.BigEndian.Uint32(data[8:12]),
|
||||||
|
OutboundStreams: binary.BigEndian.Uint16(data[12:14]),
|
||||||
|
InboundStreams: binary.BigEndian.Uint16(data[14:16]),
|
||||||
|
InitialTSN: binary.BigEndian.Uint32(data[16:20]),
|
||||||
|
}
|
||||||
|
paramData := data[20:sc.ActualLength]
|
||||||
|
for len(paramData) > 0 {
|
||||||
|
p := SCTPInitParameter(decodeSCTPParameter(paramData))
|
||||||
|
paramData = paramData[p.ActualLength:]
|
||||||
|
sc.Parameters = append(sc.Parameters, p)
|
||||||
|
}
|
||||||
|
p.AddLayer(sc)
|
||||||
|
return p.NextDecoder(gopacket.DecodeFunc(decodeWithSCTPChunkTypePrefix))
|
||||||
|
}
|
||||||
|
|
||||||
|
// SerializeTo is for gopacket.SerializableLayer.
|
||||||
|
func (sc SCTPInit) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
|
||||||
|
var payload []byte
|
||||||
|
for _, param := range sc.Parameters {
|
||||||
|
payload = append(payload, SCTPParameter(param).Bytes()...)
|
||||||
|
}
|
||||||
|
length := 20 + len(payload)
|
||||||
|
bytes, err := b.PrependBytes(roundUpToNearest4(length))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
bytes[0] = uint8(sc.Type)
|
||||||
|
bytes[1] = sc.Flags
|
||||||
|
binary.BigEndian.PutUint16(bytes[2:4], uint16(length))
|
||||||
|
binary.BigEndian.PutUint32(bytes[4:8], sc.InitiateTag)
|
||||||
|
binary.BigEndian.PutUint32(bytes[8:12], sc.AdvertisedReceiverWindowCredit)
|
||||||
|
binary.BigEndian.PutUint16(bytes[12:14], sc.OutboundStreams)
|
||||||
|
binary.BigEndian.PutUint16(bytes[14:16], sc.InboundStreams)
|
||||||
|
binary.BigEndian.PutUint32(bytes[16:20], sc.InitialTSN)
|
||||||
|
copy(bytes[20:], payload)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SCTPSack is the SCTP Selective ACK chunk layer.
|
||||||
|
type SCTPSack struct {
|
||||||
|
SCTPChunk
|
||||||
|
CumulativeTSNAck uint32
|
||||||
|
AdvertisedReceiverWindowCredit uint32
|
||||||
|
NumGapACKs, NumDuplicateTSNs uint16
|
||||||
|
GapACKs []uint16
|
||||||
|
DuplicateTSNs []uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
// LayerType return LayerTypeSCTPSack
|
||||||
|
func (sc *SCTPSack) LayerType() gopacket.LayerType {
|
||||||
|
return LayerTypeSCTPSack
|
||||||
|
}
|
||||||
|
|
||||||
|
func decodeSCTPSack(data []byte, p gopacket.PacketBuilder) error {
|
||||||
|
chunk, err := decodeSCTPChunk(data)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
sc := &SCTPSack{
|
||||||
|
SCTPChunk: chunk,
|
||||||
|
CumulativeTSNAck: binary.BigEndian.Uint32(data[4:8]),
|
||||||
|
AdvertisedReceiverWindowCredit: binary.BigEndian.Uint32(data[8:12]),
|
||||||
|
NumGapACKs: binary.BigEndian.Uint16(data[12:14]),
|
||||||
|
NumDuplicateTSNs: binary.BigEndian.Uint16(data[14:16]),
|
||||||
|
}
|
||||||
|
// We maximize gapAcks and dupTSNs here so we're not allocating tons
|
||||||
|
// of memory based on a user-controlable field. Our maximums are not exact,
|
||||||
|
// but should give us sane defaults... we'll still hit slice boundaries and
|
||||||
|
// fail if the user-supplied values are too high (in the for loops below), but
|
||||||
|
// the amount of memory we'll have allocated because of that should be small
|
||||||
|
// (< sc.ActualLength)
|
||||||
|
gapAcks := sc.SCTPChunk.ActualLength / 2
|
||||||
|
dupTSNs := (sc.SCTPChunk.ActualLength - gapAcks*2) / 4
|
||||||
|
if gapAcks > int(sc.NumGapACKs) {
|
||||||
|
gapAcks = int(sc.NumGapACKs)
|
||||||
|
}
|
||||||
|
if dupTSNs > int(sc.NumDuplicateTSNs) {
|
||||||
|
dupTSNs = int(sc.NumDuplicateTSNs)
|
||||||
|
}
|
||||||
|
sc.GapACKs = make([]uint16, 0, gapAcks)
|
||||||
|
sc.DuplicateTSNs = make([]uint32, 0, dupTSNs)
|
||||||
|
bytesRemaining := data[16:]
|
||||||
|
for i := 0; i < int(sc.NumGapACKs); i++ {
|
||||||
|
sc.GapACKs = append(sc.GapACKs, binary.BigEndian.Uint16(bytesRemaining[:2]))
|
||||||
|
bytesRemaining = bytesRemaining[2:]
|
||||||
|
}
|
||||||
|
for i := 0; i < int(sc.NumDuplicateTSNs); i++ {
|
||||||
|
sc.DuplicateTSNs = append(sc.DuplicateTSNs, binary.BigEndian.Uint32(bytesRemaining[:4]))
|
||||||
|
bytesRemaining = bytesRemaining[4:]
|
||||||
|
}
|
||||||
|
p.AddLayer(sc)
|
||||||
|
return p.NextDecoder(gopacket.DecodeFunc(decodeWithSCTPChunkTypePrefix))
|
||||||
|
}
|
||||||
|
|
||||||
|
// SerializeTo is for gopacket.SerializableLayer.
|
||||||
|
func (sc SCTPSack) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
|
||||||
|
length := 16 + 2*len(sc.GapACKs) + 4*len(sc.DuplicateTSNs)
|
||||||
|
bytes, err := b.PrependBytes(roundUpToNearest4(length))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
bytes[0] = uint8(sc.Type)
|
||||||
|
bytes[1] = sc.Flags
|
||||||
|
binary.BigEndian.PutUint16(bytes[2:4], uint16(length))
|
||||||
|
binary.BigEndian.PutUint32(bytes[4:8], sc.CumulativeTSNAck)
|
||||||
|
binary.BigEndian.PutUint32(bytes[8:12], sc.AdvertisedReceiverWindowCredit)
|
||||||
|
binary.BigEndian.PutUint16(bytes[12:14], uint16(len(sc.GapACKs)))
|
||||||
|
binary.BigEndian.PutUint16(bytes[14:16], uint16(len(sc.DuplicateTSNs)))
|
||||||
|
for i, v := range sc.GapACKs {
|
||||||
|
binary.BigEndian.PutUint16(bytes[16+i*2:], v)
|
||||||
|
}
|
||||||
|
offset := 16 + 2*len(sc.GapACKs)
|
||||||
|
for i, v := range sc.DuplicateTSNs {
|
||||||
|
binary.BigEndian.PutUint32(bytes[offset+i*4:], v)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SCTPHeartbeatParameter is the parameter type used by SCTP heartbeat and
|
||||||
|
// heartbeat ack layers.
|
||||||
|
type SCTPHeartbeatParameter SCTPParameter
|
||||||
|
|
||||||
|
// SCTPHeartbeat is the SCTP heartbeat layer, also used for heatbeat ack.
|
||||||
|
type SCTPHeartbeat struct {
|
||||||
|
SCTPChunk
|
||||||
|
Parameters []SCTPHeartbeatParameter
|
||||||
|
}
|
||||||
|
|
||||||
|
// LayerType returns gopacket.LayerTypeSCTPHeartbeat.
|
||||||
|
func (sc *SCTPHeartbeat) LayerType() gopacket.LayerType {
|
||||||
|
if sc.Type == SCTPChunkTypeHeartbeatAck {
|
||||||
|
return LayerTypeSCTPHeartbeatAck
|
||||||
|
}
|
||||||
|
// sc.Type == SCTPChunkTypeHeartbeat
|
||||||
|
return LayerTypeSCTPHeartbeat
|
||||||
|
}
|
||||||
|
|
||||||
|
func decodeSCTPHeartbeat(data []byte, p gopacket.PacketBuilder) error {
|
||||||
|
chunk, err := decodeSCTPChunk(data)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
sc := &SCTPHeartbeat{
|
||||||
|
SCTPChunk: chunk,
|
||||||
|
}
|
||||||
|
paramData := data[4:sc.Length]
|
||||||
|
for len(paramData) > 0 {
|
||||||
|
p := SCTPHeartbeatParameter(decodeSCTPParameter(paramData))
|
||||||
|
paramData = paramData[p.ActualLength:]
|
||||||
|
sc.Parameters = append(sc.Parameters, p)
|
||||||
|
}
|
||||||
|
p.AddLayer(sc)
|
||||||
|
return p.NextDecoder(gopacket.DecodeFunc(decodeWithSCTPChunkTypePrefix))
|
||||||
|
}
|
||||||
|
|
||||||
|
// SerializeTo is for gopacket.SerializableLayer.
|
||||||
|
func (sc SCTPHeartbeat) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
|
||||||
|
var payload []byte
|
||||||
|
for _, param := range sc.Parameters {
|
||||||
|
payload = append(payload, SCTPParameter(param).Bytes()...)
|
||||||
|
}
|
||||||
|
length := 4 + len(payload)
|
||||||
|
|
||||||
|
bytes, err := b.PrependBytes(roundUpToNearest4(length))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
bytes[0] = uint8(sc.Type)
|
||||||
|
bytes[1] = sc.Flags
|
||||||
|
binary.BigEndian.PutUint16(bytes[2:4], uint16(length))
|
||||||
|
copy(bytes[4:], payload)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SCTPErrorParameter is the parameter type used by SCTP Abort and Error layers.
|
||||||
|
type SCTPErrorParameter SCTPParameter
|
||||||
|
|
||||||
|
// SCTPError is the SCTP error layer, also used for SCTP aborts.
|
||||||
|
type SCTPError struct {
|
||||||
|
SCTPChunk
|
||||||
|
Parameters []SCTPErrorParameter
|
||||||
|
}
|
||||||
|
|
||||||
|
// LayerType returns LayerTypeSCTPAbort or LayerTypeSCTPError.
|
||||||
|
func (sc *SCTPError) LayerType() gopacket.LayerType {
|
||||||
|
if sc.Type == SCTPChunkTypeAbort {
|
||||||
|
return LayerTypeSCTPAbort
|
||||||
|
}
|
||||||
|
// sc.Type == SCTPChunkTypeError
|
||||||
|
return LayerTypeSCTPError
|
||||||
|
}
|
||||||
|
|
||||||
|
func decodeSCTPError(data []byte, p gopacket.PacketBuilder) error {
|
||||||
|
// remarkably similar to decodeSCTPHeartbeat ;)
|
||||||
|
chunk, err := decodeSCTPChunk(data)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
sc := &SCTPError{
|
||||||
|
SCTPChunk: chunk,
|
||||||
|
}
|
||||||
|
paramData := data[4:sc.Length]
|
||||||
|
for len(paramData) > 0 {
|
||||||
|
p := SCTPErrorParameter(decodeSCTPParameter(paramData))
|
||||||
|
paramData = paramData[p.ActualLength:]
|
||||||
|
sc.Parameters = append(sc.Parameters, p)
|
||||||
|
}
|
||||||
|
p.AddLayer(sc)
|
||||||
|
return p.NextDecoder(gopacket.DecodeFunc(decodeWithSCTPChunkTypePrefix))
|
||||||
|
}
|
||||||
|
|
||||||
|
// SerializeTo is for gopacket.SerializableLayer.
|
||||||
|
func (sc SCTPError) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
|
||||||
|
var payload []byte
|
||||||
|
for _, param := range sc.Parameters {
|
||||||
|
payload = append(payload, SCTPParameter(param).Bytes()...)
|
||||||
|
}
|
||||||
|
length := 4 + len(payload)
|
||||||
|
|
||||||
|
bytes, err := b.PrependBytes(roundUpToNearest4(length))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
bytes[0] = uint8(sc.Type)
|
||||||
|
bytes[1] = sc.Flags
|
||||||
|
binary.BigEndian.PutUint16(bytes[2:4], uint16(length))
|
||||||
|
copy(bytes[4:], payload)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SCTPShutdown is the SCTP shutdown layer.
|
||||||
|
type SCTPShutdown struct {
|
||||||
|
SCTPChunk
|
||||||
|
CumulativeTSNAck uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
// LayerType returns gopacket.LayerTypeSCTPShutdown.
|
||||||
|
func (sc *SCTPShutdown) LayerType() gopacket.LayerType { return LayerTypeSCTPShutdown }
|
||||||
|
|
||||||
|
func decodeSCTPShutdown(data []byte, p gopacket.PacketBuilder) error {
|
||||||
|
chunk, err := decodeSCTPChunk(data)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
sc := &SCTPShutdown{
|
||||||
|
SCTPChunk: chunk,
|
||||||
|
CumulativeTSNAck: binary.BigEndian.Uint32(data[4:8]),
|
||||||
|
}
|
||||||
|
p.AddLayer(sc)
|
||||||
|
return p.NextDecoder(gopacket.DecodeFunc(decodeWithSCTPChunkTypePrefix))
|
||||||
|
}
|
||||||
|
|
||||||
|
// SerializeTo is for gopacket.SerializableLayer.
|
||||||
|
func (sc SCTPShutdown) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
|
||||||
|
bytes, err := b.PrependBytes(8)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
bytes[0] = uint8(sc.Type)
|
||||||
|
bytes[1] = sc.Flags
|
||||||
|
binary.BigEndian.PutUint16(bytes[2:4], 8)
|
||||||
|
binary.BigEndian.PutUint32(bytes[4:8], sc.CumulativeTSNAck)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SCTPShutdownAck is the SCTP shutdown layer.
|
||||||
|
type SCTPShutdownAck struct {
|
||||||
|
SCTPChunk
|
||||||
|
}
|
||||||
|
|
||||||
|
// LayerType returns gopacket.LayerTypeSCTPShutdownAck.
|
||||||
|
func (sc *SCTPShutdownAck) LayerType() gopacket.LayerType { return LayerTypeSCTPShutdownAck }
|
||||||
|
|
||||||
|
func decodeSCTPShutdownAck(data []byte, p gopacket.PacketBuilder) error {
|
||||||
|
chunk, err := decodeSCTPChunk(data)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
sc := &SCTPShutdownAck{
|
||||||
|
SCTPChunk: chunk,
|
||||||
|
}
|
||||||
|
p.AddLayer(sc)
|
||||||
|
return p.NextDecoder(gopacket.DecodeFunc(decodeWithSCTPChunkTypePrefix))
|
||||||
|
}
|
||||||
|
|
||||||
|
// SerializeTo is for gopacket.SerializableLayer.
|
||||||
|
func (sc SCTPShutdownAck) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
|
||||||
|
bytes, err := b.PrependBytes(4)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
bytes[0] = uint8(sc.Type)
|
||||||
|
bytes[1] = sc.Flags
|
||||||
|
binary.BigEndian.PutUint16(bytes[2:4], 4)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SCTPCookieEcho is the SCTP Cookie Echo layer.
|
||||||
|
type SCTPCookieEcho struct {
|
||||||
|
SCTPChunk
|
||||||
|
Cookie []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
// LayerType returns gopacket.LayerTypeSCTPCookieEcho.
|
||||||
|
func (sc *SCTPCookieEcho) LayerType() gopacket.LayerType { return LayerTypeSCTPCookieEcho }
|
||||||
|
|
||||||
|
func decodeSCTPCookieEcho(data []byte, p gopacket.PacketBuilder) error {
|
||||||
|
chunk, err := decodeSCTPChunk(data)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
sc := &SCTPCookieEcho{
|
||||||
|
SCTPChunk: chunk,
|
||||||
|
}
|
||||||
|
sc.Cookie = data[4:sc.Length]
|
||||||
|
p.AddLayer(sc)
|
||||||
|
return p.NextDecoder(gopacket.DecodeFunc(decodeWithSCTPChunkTypePrefix))
|
||||||
|
}
|
||||||
|
|
||||||
|
// SerializeTo is for gopacket.SerializableLayer.
|
||||||
|
func (sc SCTPCookieEcho) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
|
||||||
|
length := 4 + len(sc.Cookie)
|
||||||
|
bytes, err := b.PrependBytes(roundUpToNearest4(length))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
bytes[0] = uint8(sc.Type)
|
||||||
|
bytes[1] = sc.Flags
|
||||||
|
binary.BigEndian.PutUint16(bytes[2:4], uint16(length))
|
||||||
|
copy(bytes[4:], sc.Cookie)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// This struct is used by all empty SCTP chunks (currently CookieAck and
|
||||||
|
// ShutdownComplete).
|
||||||
|
type SCTPEmptyLayer struct {
|
||||||
|
SCTPChunk
|
||||||
|
}
|
||||||
|
|
||||||
|
// LayerType returns either gopacket.LayerTypeSCTPShutdownComplete or
|
||||||
|
// LayerTypeSCTPCookieAck.
|
||||||
|
func (sc *SCTPEmptyLayer) LayerType() gopacket.LayerType {
|
||||||
|
if sc.Type == SCTPChunkTypeShutdownComplete {
|
||||||
|
return LayerTypeSCTPShutdownComplete
|
||||||
|
}
|
||||||
|
// sc.Type == SCTPChunkTypeCookieAck
|
||||||
|
return LayerTypeSCTPCookieAck
|
||||||
|
}
|
||||||
|
|
||||||
|
func decodeSCTPEmptyLayer(data []byte, p gopacket.PacketBuilder) error {
|
||||||
|
chunk, err := decodeSCTPChunk(data)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
sc := &SCTPEmptyLayer{
|
||||||
|
SCTPChunk: chunk,
|
||||||
|
}
|
||||||
|
p.AddLayer(sc)
|
||||||
|
return p.NextDecoder(gopacket.DecodeFunc(decodeWithSCTPChunkTypePrefix))
|
||||||
|
}
|
||||||
|
|
||||||
|
// SerializeTo is for gopacket.SerializableLayer.
|
||||||
|
func (sc SCTPEmptyLayer) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
|
||||||
|
bytes, err := b.PrependBytes(4)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
bytes[0] = uint8(sc.Type)
|
||||||
|
bytes[1] = sc.Flags
|
||||||
|
binary.BigEndian.PutUint16(bytes[2:4], 4)
|
||||||
|
return nil
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,542 @@
|
||||||
|
// 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 (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/google/gopacket"
|
||||||
|
)
|
||||||
|
|
||||||
|
// SIPVersion defines the different versions of the SIP Protocol
|
||||||
|
type SIPVersion uint8
|
||||||
|
|
||||||
|
// Represents all the versions of SIP protocol
|
||||||
|
const (
|
||||||
|
SIPVersion1 SIPVersion = 1
|
||||||
|
SIPVersion2 SIPVersion = 2
|
||||||
|
)
|
||||||
|
|
||||||
|
func (sv SIPVersion) String() string {
|
||||||
|
switch sv {
|
||||||
|
default:
|
||||||
|
// Defaulting to SIP/2.0
|
||||||
|
return "SIP/2.0"
|
||||||
|
case SIPVersion1:
|
||||||
|
return "SIP/1.0"
|
||||||
|
case SIPVersion2:
|
||||||
|
return "SIP/2.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetSIPVersion is used to get SIP version constant
|
||||||
|
func GetSIPVersion(version string) (SIPVersion, error) {
|
||||||
|
switch strings.ToUpper(version) {
|
||||||
|
case "SIP/1.0":
|
||||||
|
return SIPVersion1, nil
|
||||||
|
case "SIP/2.0":
|
||||||
|
return SIPVersion2, nil
|
||||||
|
default:
|
||||||
|
return 0, fmt.Errorf("Unknown SIP version: '%s'", version)
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SIPMethod defines the different methods of the SIP Protocol
|
||||||
|
// defined in the different RFC's
|
||||||
|
type SIPMethod uint16
|
||||||
|
|
||||||
|
// Here are all the SIP methods
|
||||||
|
const (
|
||||||
|
SIPMethodInvite SIPMethod = 1 // INVITE [RFC3261]
|
||||||
|
SIPMethodAck SIPMethod = 2 // ACK [RFC3261]
|
||||||
|
SIPMethodBye SIPMethod = 3 // BYE [RFC3261]
|
||||||
|
SIPMethodCancel SIPMethod = 4 // CANCEL [RFC3261]
|
||||||
|
SIPMethodOptions SIPMethod = 5 // OPTIONS [RFC3261]
|
||||||
|
SIPMethodRegister SIPMethod = 6 // REGISTER [RFC3261]
|
||||||
|
SIPMethodPrack SIPMethod = 7 // PRACK [RFC3262]
|
||||||
|
SIPMethodSubscribe SIPMethod = 8 // SUBSCRIBE [RFC6665]
|
||||||
|
SIPMethodNotify SIPMethod = 9 // NOTIFY [RFC6665]
|
||||||
|
SIPMethodPublish SIPMethod = 10 // PUBLISH [RFC3903]
|
||||||
|
SIPMethodInfo SIPMethod = 11 // INFO [RFC6086]
|
||||||
|
SIPMethodRefer SIPMethod = 12 // REFER [RFC3515]
|
||||||
|
SIPMethodMessage SIPMethod = 13 // MESSAGE [RFC3428]
|
||||||
|
SIPMethodUpdate SIPMethod = 14 // UPDATE [RFC3311]
|
||||||
|
SIPMethodPing SIPMethod = 15 // PING [https://tools.ietf.org/html/draft-fwmiller-ping-03]
|
||||||
|
)
|
||||||
|
|
||||||
|
func (sm SIPMethod) String() string {
|
||||||
|
switch sm {
|
||||||
|
default:
|
||||||
|
return "Unknown method"
|
||||||
|
case SIPMethodInvite:
|
||||||
|
return "INVITE"
|
||||||
|
case SIPMethodAck:
|
||||||
|
return "ACK"
|
||||||
|
case SIPMethodBye:
|
||||||
|
return "BYE"
|
||||||
|
case SIPMethodCancel:
|
||||||
|
return "CANCEL"
|
||||||
|
case SIPMethodOptions:
|
||||||
|
return "OPTIONS"
|
||||||
|
case SIPMethodRegister:
|
||||||
|
return "REGISTER"
|
||||||
|
case SIPMethodPrack:
|
||||||
|
return "PRACK"
|
||||||
|
case SIPMethodSubscribe:
|
||||||
|
return "SUBSCRIBE"
|
||||||
|
case SIPMethodNotify:
|
||||||
|
return "NOTIFY"
|
||||||
|
case SIPMethodPublish:
|
||||||
|
return "PUBLISH"
|
||||||
|
case SIPMethodInfo:
|
||||||
|
return "INFO"
|
||||||
|
case SIPMethodRefer:
|
||||||
|
return "REFER"
|
||||||
|
case SIPMethodMessage:
|
||||||
|
return "MESSAGE"
|
||||||
|
case SIPMethodUpdate:
|
||||||
|
return "UPDATE"
|
||||||
|
case SIPMethodPing:
|
||||||
|
return "PING"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetSIPMethod returns the constant of a SIP method
|
||||||
|
// from its string
|
||||||
|
func GetSIPMethod(method string) (SIPMethod, error) {
|
||||||
|
switch strings.ToUpper(method) {
|
||||||
|
case "INVITE":
|
||||||
|
return SIPMethodInvite, nil
|
||||||
|
case "ACK":
|
||||||
|
return SIPMethodAck, nil
|
||||||
|
case "BYE":
|
||||||
|
return SIPMethodBye, nil
|
||||||
|
case "CANCEL":
|
||||||
|
return SIPMethodCancel, nil
|
||||||
|
case "OPTIONS":
|
||||||
|
return SIPMethodOptions, nil
|
||||||
|
case "REGISTER":
|
||||||
|
return SIPMethodRegister, nil
|
||||||
|
case "PRACK":
|
||||||
|
return SIPMethodPrack, nil
|
||||||
|
case "SUBSCRIBE":
|
||||||
|
return SIPMethodSubscribe, nil
|
||||||
|
case "NOTIFY":
|
||||||
|
return SIPMethodNotify, nil
|
||||||
|
case "PUBLISH":
|
||||||
|
return SIPMethodPublish, nil
|
||||||
|
case "INFO":
|
||||||
|
return SIPMethodInfo, nil
|
||||||
|
case "REFER":
|
||||||
|
return SIPMethodRefer, nil
|
||||||
|
case "MESSAGE":
|
||||||
|
return SIPMethodMessage, nil
|
||||||
|
case "UPDATE":
|
||||||
|
return SIPMethodUpdate, nil
|
||||||
|
case "PING":
|
||||||
|
return SIPMethodPing, nil
|
||||||
|
default:
|
||||||
|
return 0, fmt.Errorf("Unknown SIP method: '%s'", method)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Here is a correspondance between long header names and short
|
||||||
|
// as defined in rfc3261 in section 20
|
||||||
|
var compactSipHeadersCorrespondance = map[string]string{
|
||||||
|
"accept-contact": "a",
|
||||||
|
"allow-events": "u",
|
||||||
|
"call-id": "i",
|
||||||
|
"contact": "m",
|
||||||
|
"content-encoding": "e",
|
||||||
|
"content-length": "l",
|
||||||
|
"content-type": "c",
|
||||||
|
"event": "o",
|
||||||
|
"from": "f",
|
||||||
|
"identity": "y",
|
||||||
|
"refer-to": "r",
|
||||||
|
"referred-by": "b",
|
||||||
|
"reject-contact": "j",
|
||||||
|
"request-disposition": "d",
|
||||||
|
"session-expires": "x",
|
||||||
|
"subject": "s",
|
||||||
|
"supported": "k",
|
||||||
|
"to": "t",
|
||||||
|
"via": "v",
|
||||||
|
}
|
||||||
|
|
||||||
|
// SIP object will contains information about decoded SIP packet.
|
||||||
|
// -> The SIP Version
|
||||||
|
// -> The SIP Headers (in a map[string][]string because of multiple headers with the same name
|
||||||
|
// -> The SIP Method
|
||||||
|
// -> The SIP Response code (if it's a response)
|
||||||
|
// -> The SIP Status line (if it's a response)
|
||||||
|
// You can easily know the type of the packet with the IsResponse boolean
|
||||||
|
//
|
||||||
|
type SIP struct {
|
||||||
|
BaseLayer
|
||||||
|
|
||||||
|
// Base information
|
||||||
|
Version SIPVersion
|
||||||
|
Method SIPMethod
|
||||||
|
Headers map[string][]string
|
||||||
|
|
||||||
|
// Request
|
||||||
|
RequestURI string
|
||||||
|
|
||||||
|
// Response
|
||||||
|
IsResponse bool
|
||||||
|
ResponseCode int
|
||||||
|
ResponseStatus string
|
||||||
|
|
||||||
|
// Private fields
|
||||||
|
cseq int64
|
||||||
|
contentLength int64
|
||||||
|
lastHeaderParsed string
|
||||||
|
}
|
||||||
|
|
||||||
|
// decodeSIP decodes the byte slice into a SIP type. It also
|
||||||
|
// setups the application Layer in PacketBuilder.
|
||||||
|
func decodeSIP(data []byte, p gopacket.PacketBuilder) error {
|
||||||
|
s := NewSIP()
|
||||||
|
err := s.DecodeFromBytes(data, p)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
p.AddLayer(s)
|
||||||
|
p.SetApplicationLayer(s)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewSIP instantiates a new empty SIP object
|
||||||
|
func NewSIP() *SIP {
|
||||||
|
s := new(SIP)
|
||||||
|
s.Headers = make(map[string][]string)
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
// LayerType returns gopacket.LayerTypeSIP.
|
||||||
|
func (s *SIP) LayerType() gopacket.LayerType {
|
||||||
|
return LayerTypeSIP
|
||||||
|
}
|
||||||
|
|
||||||
|
// Payload returns the base layer payload
|
||||||
|
func (s *SIP) Payload() []byte {
|
||||||
|
return s.BaseLayer.Payload
|
||||||
|
}
|
||||||
|
|
||||||
|
// CanDecode returns the set of layer types that this DecodingLayer can decode
|
||||||
|
func (s *SIP) CanDecode() gopacket.LayerClass {
|
||||||
|
return LayerTypeSIP
|
||||||
|
}
|
||||||
|
|
||||||
|
// NextLayerType returns the layer type contained by this DecodingLayer
|
||||||
|
func (s *SIP) NextLayerType() gopacket.LayerType {
|
||||||
|
return gopacket.LayerTypePayload
|
||||||
|
}
|
||||||
|
|
||||||
|
// DecodeFromBytes decodes the slice into the SIP struct.
|
||||||
|
func (s *SIP) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
|
||||||
|
// Init some vars for parsing follow-up
|
||||||
|
var countLines int
|
||||||
|
var line []byte
|
||||||
|
var err error
|
||||||
|
var offset int
|
||||||
|
|
||||||
|
// Iterate on all lines of the SIP Headers
|
||||||
|
// and stop when we reach the SDP (aka when the new line
|
||||||
|
// is at index 0 of the remaining packet)
|
||||||
|
buffer := bytes.NewBuffer(data)
|
||||||
|
|
||||||
|
for {
|
||||||
|
|
||||||
|
// Read next line
|
||||||
|
line, err = buffer.ReadBytes(byte('\n'))
|
||||||
|
if err != nil {
|
||||||
|
if err == io.EOF {
|
||||||
|
if len(bytes.Trim(line, "\r\n")) > 0 {
|
||||||
|
df.SetTruncated()
|
||||||
|
}
|
||||||
|
break
|
||||||
|
} else {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
offset += len(line)
|
||||||
|
|
||||||
|
// Trim the new line delimiters
|
||||||
|
line = bytes.Trim(line, "\r\n")
|
||||||
|
|
||||||
|
// Empty line, we hit Body
|
||||||
|
if len(line) == 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
// First line is the SIP request/response line
|
||||||
|
// Other lines are headers
|
||||||
|
if countLines == 0 {
|
||||||
|
err = s.ParseFirstLine(line)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
err = s.ParseHeader(line)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
countLines++
|
||||||
|
}
|
||||||
|
s.BaseLayer = BaseLayer{Contents: data[:offset], Payload: data[offset:]}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseFirstLine will compute the first line of a SIP packet.
|
||||||
|
// The first line will tell us if it's a request or a response.
|
||||||
|
//
|
||||||
|
// Examples of first line of SIP Prococol :
|
||||||
|
//
|
||||||
|
// Request : INVITE bob@example.com SIP/2.0
|
||||||
|
// Response : SIP/2.0 200 OK
|
||||||
|
// Response : SIP/2.0 501 Not Implemented
|
||||||
|
//
|
||||||
|
func (s *SIP) ParseFirstLine(firstLine []byte) error {
|
||||||
|
|
||||||
|
var err error
|
||||||
|
|
||||||
|
// Splits line by space
|
||||||
|
splits := strings.SplitN(string(firstLine), " ", 3)
|
||||||
|
|
||||||
|
// We must have at least 3 parts
|
||||||
|
if len(splits) < 3 {
|
||||||
|
return fmt.Errorf("invalid first SIP line: '%s'", string(firstLine))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Determine the SIP packet type
|
||||||
|
if strings.HasPrefix(splits[0], "SIP") {
|
||||||
|
|
||||||
|
// --> Response
|
||||||
|
s.IsResponse = true
|
||||||
|
|
||||||
|
// Validate SIP Version
|
||||||
|
s.Version, err = GetSIPVersion(splits[0])
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compute code
|
||||||
|
s.ResponseCode, err = strconv.Atoi(splits[1])
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compute status line
|
||||||
|
s.ResponseStatus = splits[2]
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
// --> Request
|
||||||
|
|
||||||
|
// Validate method
|
||||||
|
s.Method, err = GetSIPMethod(splits[0])
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
s.RequestURI = splits[1]
|
||||||
|
|
||||||
|
// Validate SIP Version
|
||||||
|
s.Version, err = GetSIPVersion(splits[2])
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseHeader will parse a SIP Header
|
||||||
|
// SIP Headers are quite simple, there are colon separated name and value
|
||||||
|
// Headers can be spread over multiple lines
|
||||||
|
//
|
||||||
|
// Examples of header :
|
||||||
|
//
|
||||||
|
// CSeq: 1 REGISTER
|
||||||
|
// Via: SIP/2.0/UDP there.com:5060
|
||||||
|
// Authorization:Digest username="UserB",
|
||||||
|
// realm="MCI WorldCom SIP",
|
||||||
|
// nonce="1cec4341ae6cbe5a359ea9c8e88df84f", opaque="",
|
||||||
|
// uri="sip:ss2.wcom.com", response="71ba27c64bd01de719686aa4590d5824"
|
||||||
|
//
|
||||||
|
func (s *SIP) ParseHeader(header []byte) (err error) {
|
||||||
|
|
||||||
|
// Ignore empty headers
|
||||||
|
if len(header) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if this is the following of last header
|
||||||
|
// RFC 3261 - 7.3.1 - Header Field Format specify that following lines of
|
||||||
|
// multiline headers must begin by SP or TAB
|
||||||
|
if header[0] == '\t' || header[0] == ' ' {
|
||||||
|
|
||||||
|
header = bytes.TrimSpace(header)
|
||||||
|
s.Headers[s.lastHeaderParsed][len(s.Headers[s.lastHeaderParsed])-1] += fmt.Sprintf(" %s", string(header))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find the ':' to separate header name and value
|
||||||
|
index := bytes.Index(header, []byte(":"))
|
||||||
|
if index >= 0 {
|
||||||
|
|
||||||
|
headerName := strings.ToLower(string(bytes.Trim(header[:index], " ")))
|
||||||
|
headerValue := string(bytes.Trim(header[index+1:], " "))
|
||||||
|
|
||||||
|
// Add header to object
|
||||||
|
s.Headers[headerName] = append(s.Headers[headerName], headerValue)
|
||||||
|
s.lastHeaderParsed = headerName
|
||||||
|
|
||||||
|
// Compute specific headers
|
||||||
|
err = s.ParseSpecificHeaders(headerName, headerValue)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseSpecificHeaders will parse some specific key values from
|
||||||
|
// specific headers like CSeq or Content-Length integer values
|
||||||
|
func (s *SIP) ParseSpecificHeaders(headerName string, headerValue string) (err error) {
|
||||||
|
|
||||||
|
switch headerName {
|
||||||
|
case "cseq":
|
||||||
|
|
||||||
|
// CSeq header value is formatted like that :
|
||||||
|
// CSeq: 123 INVITE
|
||||||
|
// We split the value to parse Cseq integer value, and method
|
||||||
|
splits := strings.Split(headerValue, " ")
|
||||||
|
if len(splits) > 1 {
|
||||||
|
|
||||||
|
// Parse Cseq
|
||||||
|
s.cseq, err = strconv.ParseInt(splits[0], 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate method
|
||||||
|
if s.IsResponse {
|
||||||
|
s.Method, err = GetSIPMethod(splits[1])
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case "content-length":
|
||||||
|
|
||||||
|
// Parse Content-Length
|
||||||
|
s.contentLength, err = strconv.ParseInt(headerValue, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetAllHeaders will return the full headers of the
|
||||||
|
// current SIP packets in a map[string][]string
|
||||||
|
func (s *SIP) GetAllHeaders() map[string][]string {
|
||||||
|
return s.Headers
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetHeader will return all the headers with
|
||||||
|
// the specified name.
|
||||||
|
func (s *SIP) GetHeader(headerName string) []string {
|
||||||
|
headerName = strings.ToLower(headerName)
|
||||||
|
h := make([]string, 0)
|
||||||
|
if _, ok := s.Headers[headerName]; ok {
|
||||||
|
return s.Headers[headerName]
|
||||||
|
}
|
||||||
|
compactHeader := compactSipHeadersCorrespondance[headerName]
|
||||||
|
if _, ok := s.Headers[compactHeader]; ok {
|
||||||
|
return s.Headers[compactHeader]
|
||||||
|
}
|
||||||
|
return h
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetFirstHeader will return the first header with
|
||||||
|
// the specified name. If the current SIP packet has multiple
|
||||||
|
// headers with the same name, it returns the first.
|
||||||
|
func (s *SIP) GetFirstHeader(headerName string) string {
|
||||||
|
headers := s.GetHeader(headerName)
|
||||||
|
if len(headers) > 0 {
|
||||||
|
return headers[0]
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Some handy getters for most used SIP headers
|
||||||
|
//
|
||||||
|
|
||||||
|
// GetAuthorization will return the Authorization
|
||||||
|
// header of the current SIP packet
|
||||||
|
func (s *SIP) GetAuthorization() string {
|
||||||
|
return s.GetFirstHeader("Authorization")
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetFrom will return the From
|
||||||
|
// header of the current SIP packet
|
||||||
|
func (s *SIP) GetFrom() string {
|
||||||
|
return s.GetFirstHeader("From")
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetTo will return the To
|
||||||
|
// header of the current SIP packet
|
||||||
|
func (s *SIP) GetTo() string {
|
||||||
|
return s.GetFirstHeader("To")
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetContact will return the Contact
|
||||||
|
// header of the current SIP packet
|
||||||
|
func (s *SIP) GetContact() string {
|
||||||
|
return s.GetFirstHeader("Contact")
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetCallID will return the Call-ID
|
||||||
|
// header of the current SIP packet
|
||||||
|
func (s *SIP) GetCallID() string {
|
||||||
|
return s.GetFirstHeader("Call-ID")
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetUserAgent will return the User-Agent
|
||||||
|
// header of the current SIP packet
|
||||||
|
func (s *SIP) GetUserAgent() string {
|
||||||
|
return s.GetFirstHeader("User-Agent")
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetContentLength will return the parsed integer
|
||||||
|
// Content-Length header of the current SIP packet
|
||||||
|
func (s *SIP) GetContentLength() int64 {
|
||||||
|
return s.contentLength
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetCSeq will return the parsed integer CSeq header
|
||||||
|
// header of the current SIP packet
|
||||||
|
func (s *SIP) GetCSeq() int64 {
|
||||||
|
return s.cseq
|
||||||
|
}
|
|
@ -0,0 +1,27 @@
|
||||||
|
// 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 (
|
||||||
|
"github.com/google/gopacket"
|
||||||
|
)
|
||||||
|
|
||||||
|
// STP decode spanning tree protocol packets to transport BPDU (bridge protocol data unit) message.
|
||||||
|
type STP struct {
|
||||||
|
BaseLayer
|
||||||
|
}
|
||||||
|
|
||||||
|
// LayerType returns gopacket.LayerTypeSTP.
|
||||||
|
func (s *STP) LayerType() gopacket.LayerType { return LayerTypeSTP }
|
||||||
|
|
||||||
|
func decodeSTP(data []byte, p gopacket.PacketBuilder) error {
|
||||||
|
stp := &STP{}
|
||||||
|
stp.Contents = data[:]
|
||||||
|
// TODO: parse the STP protocol into actual subfields.
|
||||||
|
p.AddLayer(stp)
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,341 @@
|
||||||
|
// 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"
|
||||||
|
|
||||||
|
"github.com/google/gopacket"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TCP is the layer for TCP headers.
|
||||||
|
type TCP struct {
|
||||||
|
BaseLayer
|
||||||
|
SrcPort, DstPort TCPPort
|
||||||
|
Seq uint32
|
||||||
|
Ack uint32
|
||||||
|
DataOffset uint8
|
||||||
|
FIN, SYN, RST, PSH, ACK, URG, ECE, CWR, NS bool
|
||||||
|
Window uint16
|
||||||
|
Checksum uint16
|
||||||
|
Urgent uint16
|
||||||
|
sPort, dPort []byte
|
||||||
|
Options []TCPOption
|
||||||
|
Padding []byte
|
||||||
|
opts [4]TCPOption
|
||||||
|
tcpipchecksum
|
||||||
|
}
|
||||||
|
|
||||||
|
// TCPOptionKind represents a TCP option code.
|
||||||
|
type TCPOptionKind uint8
|
||||||
|
|
||||||
|
const (
|
||||||
|
TCPOptionKindEndList = 0
|
||||||
|
TCPOptionKindNop = 1
|
||||||
|
TCPOptionKindMSS = 2 // len = 4
|
||||||
|
TCPOptionKindWindowScale = 3 // len = 3
|
||||||
|
TCPOptionKindSACKPermitted = 4 // len = 2
|
||||||
|
TCPOptionKindSACK = 5 // len = n
|
||||||
|
TCPOptionKindEcho = 6 // len = 6, obsolete
|
||||||
|
TCPOptionKindEchoReply = 7 // len = 6, obsolete
|
||||||
|
TCPOptionKindTimestamps = 8 // len = 10
|
||||||
|
TCPOptionKindPartialOrderConnectionPermitted = 9 // len = 2, obsolete
|
||||||
|
TCPOptionKindPartialOrderServiceProfile = 10 // len = 3, obsolete
|
||||||
|
TCPOptionKindCC = 11 // obsolete
|
||||||
|
TCPOptionKindCCNew = 12 // obsolete
|
||||||
|
TCPOptionKindCCEcho = 13 // obsolete
|
||||||
|
TCPOptionKindAltChecksum = 14 // len = 3, obsolete
|
||||||
|
TCPOptionKindAltChecksumData = 15 // len = n, obsolete
|
||||||
|
)
|
||||||
|
|
||||||
|
func (k TCPOptionKind) String() string {
|
||||||
|
switch k {
|
||||||
|
case TCPOptionKindEndList:
|
||||||
|
return "EndList"
|
||||||
|
case TCPOptionKindNop:
|
||||||
|
return "NOP"
|
||||||
|
case TCPOptionKindMSS:
|
||||||
|
return "MSS"
|
||||||
|
case TCPOptionKindWindowScale:
|
||||||
|
return "WindowScale"
|
||||||
|
case TCPOptionKindSACKPermitted:
|
||||||
|
return "SACKPermitted"
|
||||||
|
case TCPOptionKindSACK:
|
||||||
|
return "SACK"
|
||||||
|
case TCPOptionKindEcho:
|
||||||
|
return "Echo"
|
||||||
|
case TCPOptionKindEchoReply:
|
||||||
|
return "EchoReply"
|
||||||
|
case TCPOptionKindTimestamps:
|
||||||
|
return "Timestamps"
|
||||||
|
case TCPOptionKindPartialOrderConnectionPermitted:
|
||||||
|
return "PartialOrderConnectionPermitted"
|
||||||
|
case TCPOptionKindPartialOrderServiceProfile:
|
||||||
|
return "PartialOrderServiceProfile"
|
||||||
|
case TCPOptionKindCC:
|
||||||
|
return "CC"
|
||||||
|
case TCPOptionKindCCNew:
|
||||||
|
return "CCNew"
|
||||||
|
case TCPOptionKindCCEcho:
|
||||||
|
return "CCEcho"
|
||||||
|
case TCPOptionKindAltChecksum:
|
||||||
|
return "AltChecksum"
|
||||||
|
case TCPOptionKindAltChecksumData:
|
||||||
|
return "AltChecksumData"
|
||||||
|
default:
|
||||||
|
return fmt.Sprintf("Unknown(%d)", k)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type TCPOption struct {
|
||||||
|
OptionType TCPOptionKind
|
||||||
|
OptionLength uint8
|
||||||
|
OptionData []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t TCPOption) String() string {
|
||||||
|
hd := hex.EncodeToString(t.OptionData)
|
||||||
|
if len(hd) > 0 {
|
||||||
|
hd = " 0x" + hd
|
||||||
|
}
|
||||||
|
switch t.OptionType {
|
||||||
|
case TCPOptionKindMSS:
|
||||||
|
if len(t.OptionData) >= 2 {
|
||||||
|
return fmt.Sprintf("TCPOption(%s:%v%s)",
|
||||||
|
t.OptionType,
|
||||||
|
binary.BigEndian.Uint16(t.OptionData),
|
||||||
|
hd)
|
||||||
|
}
|
||||||
|
|
||||||
|
case TCPOptionKindTimestamps:
|
||||||
|
if len(t.OptionData) == 8 {
|
||||||
|
return fmt.Sprintf("TCPOption(%s:%v/%v%s)",
|
||||||
|
t.OptionType,
|
||||||
|
binary.BigEndian.Uint32(t.OptionData[:4]),
|
||||||
|
binary.BigEndian.Uint32(t.OptionData[4:8]),
|
||||||
|
hd)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("TCPOption(%s:%s)", t.OptionType, hd)
|
||||||
|
}
|
||||||
|
|
||||||
|
// LayerType returns gopacket.LayerTypeTCP
|
||||||
|
func (t *TCP) LayerType() gopacket.LayerType { return LayerTypeTCP }
|
||||||
|
|
||||||
|
// SerializeTo writes the serialized form of this layer into the
|
||||||
|
// SerializationBuffer, implementing gopacket.SerializableLayer.
|
||||||
|
// See the docs for gopacket.SerializableLayer for more info.
|
||||||
|
func (t *TCP) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
|
||||||
|
var optionLength int
|
||||||
|
for _, o := range t.Options {
|
||||||
|
switch o.OptionType {
|
||||||
|
case 0, 1:
|
||||||
|
optionLength += 1
|
||||||
|
default:
|
||||||
|
optionLength += 2 + len(o.OptionData)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if opts.FixLengths {
|
||||||
|
if rem := optionLength % 4; rem != 0 {
|
||||||
|
t.Padding = lotsOfZeros[:4-rem]
|
||||||
|
}
|
||||||
|
t.DataOffset = uint8((len(t.Padding) + optionLength + 20) / 4)
|
||||||
|
}
|
||||||
|
bytes, err := b.PrependBytes(20 + optionLength + len(t.Padding))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
binary.BigEndian.PutUint16(bytes, uint16(t.SrcPort))
|
||||||
|
binary.BigEndian.PutUint16(bytes[2:], uint16(t.DstPort))
|
||||||
|
binary.BigEndian.PutUint32(bytes[4:], t.Seq)
|
||||||
|
binary.BigEndian.PutUint32(bytes[8:], t.Ack)
|
||||||
|
binary.BigEndian.PutUint16(bytes[12:], t.flagsAndOffset())
|
||||||
|
binary.BigEndian.PutUint16(bytes[14:], t.Window)
|
||||||
|
binary.BigEndian.PutUint16(bytes[18:], t.Urgent)
|
||||||
|
start := 20
|
||||||
|
for _, o := range t.Options {
|
||||||
|
bytes[start] = byte(o.OptionType)
|
||||||
|
switch o.OptionType {
|
||||||
|
case 0, 1:
|
||||||
|
start++
|
||||||
|
default:
|
||||||
|
if opts.FixLengths {
|
||||||
|
o.OptionLength = uint8(len(o.OptionData) + 2)
|
||||||
|
}
|
||||||
|
bytes[start+1] = o.OptionLength
|
||||||
|
copy(bytes[start+2:start+len(o.OptionData)+2], o.OptionData)
|
||||||
|
start += len(o.OptionData) + 2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
copy(bytes[start:], t.Padding)
|
||||||
|
if opts.ComputeChecksums {
|
||||||
|
// zero out checksum bytes in current serialization.
|
||||||
|
bytes[16] = 0
|
||||||
|
bytes[17] = 0
|
||||||
|
csum, err := t.computeChecksum(b.Bytes(), IPProtocolTCP)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
t.Checksum = csum
|
||||||
|
}
|
||||||
|
binary.BigEndian.PutUint16(bytes[16:], t.Checksum)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *TCP) ComputeChecksum() (uint16, error) {
|
||||||
|
return t.computeChecksum(append(t.Contents, t.Payload...), IPProtocolTCP)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *TCP) flagsAndOffset() uint16 {
|
||||||
|
f := uint16(t.DataOffset) << 12
|
||||||
|
if t.FIN {
|
||||||
|
f |= 0x0001
|
||||||
|
}
|
||||||
|
if t.SYN {
|
||||||
|
f |= 0x0002
|
||||||
|
}
|
||||||
|
if t.RST {
|
||||||
|
f |= 0x0004
|
||||||
|
}
|
||||||
|
if t.PSH {
|
||||||
|
f |= 0x0008
|
||||||
|
}
|
||||||
|
if t.ACK {
|
||||||
|
f |= 0x0010
|
||||||
|
}
|
||||||
|
if t.URG {
|
||||||
|
f |= 0x0020
|
||||||
|
}
|
||||||
|
if t.ECE {
|
||||||
|
f |= 0x0040
|
||||||
|
}
|
||||||
|
if t.CWR {
|
||||||
|
f |= 0x0080
|
||||||
|
}
|
||||||
|
if t.NS {
|
||||||
|
f |= 0x0100
|
||||||
|
}
|
||||||
|
return f
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tcp *TCP) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
|
||||||
|
if len(data) < 20 {
|
||||||
|
df.SetTruncated()
|
||||||
|
return fmt.Errorf("Invalid TCP header. Length %d less than 20", len(data))
|
||||||
|
}
|
||||||
|
tcp.SrcPort = TCPPort(binary.BigEndian.Uint16(data[0:2]))
|
||||||
|
tcp.sPort = data[0:2]
|
||||||
|
tcp.DstPort = TCPPort(binary.BigEndian.Uint16(data[2:4]))
|
||||||
|
tcp.dPort = data[2:4]
|
||||||
|
tcp.Seq = binary.BigEndian.Uint32(data[4:8])
|
||||||
|
tcp.Ack = binary.BigEndian.Uint32(data[8:12])
|
||||||
|
tcp.DataOffset = data[12] >> 4
|
||||||
|
tcp.FIN = data[13]&0x01 != 0
|
||||||
|
tcp.SYN = data[13]&0x02 != 0
|
||||||
|
tcp.RST = data[13]&0x04 != 0
|
||||||
|
tcp.PSH = data[13]&0x08 != 0
|
||||||
|
tcp.ACK = data[13]&0x10 != 0
|
||||||
|
tcp.URG = data[13]&0x20 != 0
|
||||||
|
tcp.ECE = data[13]&0x40 != 0
|
||||||
|
tcp.CWR = data[13]&0x80 != 0
|
||||||
|
tcp.NS = data[12]&0x01 != 0
|
||||||
|
tcp.Window = binary.BigEndian.Uint16(data[14:16])
|
||||||
|
tcp.Checksum = binary.BigEndian.Uint16(data[16:18])
|
||||||
|
tcp.Urgent = binary.BigEndian.Uint16(data[18:20])
|
||||||
|
if tcp.Options == nil {
|
||||||
|
// Pre-allocate to avoid allocating a slice.
|
||||||
|
tcp.Options = tcp.opts[:0]
|
||||||
|
} else {
|
||||||
|
tcp.Options = tcp.Options[:0]
|
||||||
|
}
|
||||||
|
tcp.Padding = tcp.Padding[:0]
|
||||||
|
if tcp.DataOffset < 5 {
|
||||||
|
return fmt.Errorf("Invalid TCP data offset %d < 5", tcp.DataOffset)
|
||||||
|
}
|
||||||
|
dataStart := int(tcp.DataOffset) * 4
|
||||||
|
if dataStart > len(data) {
|
||||||
|
df.SetTruncated()
|
||||||
|
tcp.Payload = nil
|
||||||
|
tcp.Contents = data
|
||||||
|
return errors.New("TCP data offset greater than packet length")
|
||||||
|
}
|
||||||
|
tcp.Contents = data[:dataStart]
|
||||||
|
tcp.Payload = data[dataStart:]
|
||||||
|
// From here on, data points just to the header options.
|
||||||
|
data = data[20:dataStart]
|
||||||
|
OPTIONS:
|
||||||
|
for len(data) > 0 {
|
||||||
|
tcp.Options = append(tcp.Options, TCPOption{OptionType: TCPOptionKind(data[0])})
|
||||||
|
opt := &tcp.Options[len(tcp.Options)-1]
|
||||||
|
switch opt.OptionType {
|
||||||
|
case TCPOptionKindEndList: // End of options
|
||||||
|
opt.OptionLength = 1
|
||||||
|
tcp.Padding = data[1:]
|
||||||
|
break OPTIONS
|
||||||
|
case TCPOptionKindNop: // 1 byte padding
|
||||||
|
opt.OptionLength = 1
|
||||||
|
default:
|
||||||
|
if len(data) < 2 {
|
||||||
|
df.SetTruncated()
|
||||||
|
return fmt.Errorf("Invalid TCP option length. Length %d less than 2", len(data))
|
||||||
|
}
|
||||||
|
opt.OptionLength = data[1]
|
||||||
|
if opt.OptionLength < 2 {
|
||||||
|
return fmt.Errorf("Invalid TCP option length %d < 2", opt.OptionLength)
|
||||||
|
} else if int(opt.OptionLength) > len(data) {
|
||||||
|
df.SetTruncated()
|
||||||
|
return fmt.Errorf("Invalid TCP option length %d exceeds remaining %d bytes", opt.OptionLength, len(data))
|
||||||
|
}
|
||||||
|
opt.OptionData = data[2:opt.OptionLength]
|
||||||
|
}
|
||||||
|
data = data[opt.OptionLength:]
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *TCP) CanDecode() gopacket.LayerClass {
|
||||||
|
return LayerTypeTCP
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *TCP) NextLayerType() gopacket.LayerType {
|
||||||
|
lt := t.DstPort.LayerType()
|
||||||
|
if lt == gopacket.LayerTypePayload {
|
||||||
|
lt = t.SrcPort.LayerType()
|
||||||
|
}
|
||||||
|
return lt
|
||||||
|
}
|
||||||
|
|
||||||
|
func decodeTCP(data []byte, p gopacket.PacketBuilder) error {
|
||||||
|
tcp := &TCP{}
|
||||||
|
err := tcp.DecodeFromBytes(data, p)
|
||||||
|
p.AddLayer(tcp)
|
||||||
|
p.SetTransportLayer(tcp)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if p.DecodeOptions().DecodeStreamsAsDatagrams {
|
||||||
|
return p.NextDecoder(tcp.NextLayerType())
|
||||||
|
} else {
|
||||||
|
return p.NextDecoder(gopacket.LayerTypePayload)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *TCP) TransportFlow() gopacket.Flow {
|
||||||
|
return gopacket.NewFlow(EndpointTCPPort, t.sPort, t.dPort)
|
||||||
|
}
|
||||||
|
|
||||||
|
// For testing only
|
||||||
|
func (t *TCP) SetInternalPortsForTesting() {
|
||||||
|
t.sPort = make([]byte, 2)
|
||||||
|
t.dPort = make([]byte, 2)
|
||||||
|
binary.BigEndian.PutUint16(t.sPort, uint16(t.SrcPort))
|
||||||
|
binary.BigEndian.PutUint16(t.dPort, uint16(t.DstPort))
|
||||||
|
}
|
|
@ -0,0 +1,104 @@
|
||||||
|
// Copyright 2012 Google, Inc. All rights reserved.
|
||||||
|
// Copyright 2009-2011 Andreas Krennmair. All rights reserved.
|
||||||
|
//
|
||||||
|
// Use of this source code is governed by a BSD-style license
|
||||||
|
// that can be found in the LICENSE file in the root of the source
|
||||||
|
// tree.
|
||||||
|
|
||||||
|
package layers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/google/gopacket"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Checksum computation for TCP/UDP.
|
||||||
|
type tcpipchecksum struct {
|
||||||
|
pseudoheader tcpipPseudoHeader
|
||||||
|
}
|
||||||
|
|
||||||
|
type tcpipPseudoHeader interface {
|
||||||
|
pseudoheaderChecksum() (uint32, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ip *IPv4) pseudoheaderChecksum() (csum uint32, err error) {
|
||||||
|
if err := ip.AddressTo4(); err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
csum += (uint32(ip.SrcIP[0]) + uint32(ip.SrcIP[2])) << 8
|
||||||
|
csum += uint32(ip.SrcIP[1]) + uint32(ip.SrcIP[3])
|
||||||
|
csum += (uint32(ip.DstIP[0]) + uint32(ip.DstIP[2])) << 8
|
||||||
|
csum += uint32(ip.DstIP[1]) + uint32(ip.DstIP[3])
|
||||||
|
return csum, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ip *IPv6) pseudoheaderChecksum() (csum uint32, err error) {
|
||||||
|
if err := ip.AddressTo16(); err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
for i := 0; i < 16; i += 2 {
|
||||||
|
csum += uint32(ip.SrcIP[i]) << 8
|
||||||
|
csum += uint32(ip.SrcIP[i+1])
|
||||||
|
csum += uint32(ip.DstIP[i]) << 8
|
||||||
|
csum += uint32(ip.DstIP[i+1])
|
||||||
|
}
|
||||||
|
return csum, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate the TCP/IP checksum defined in rfc1071. The passed-in csum is any
|
||||||
|
// initial checksum data that's already been computed.
|
||||||
|
func tcpipChecksum(data []byte, csum uint32) uint16 {
|
||||||
|
// to handle odd lengths, we loop to length - 1, incrementing by 2, then
|
||||||
|
// handle the last byte specifically by checking against the original
|
||||||
|
// length.
|
||||||
|
length := len(data) - 1
|
||||||
|
for i := 0; i < length; i += 2 {
|
||||||
|
// For our test packet, doing this manually is about 25% faster
|
||||||
|
// (740 ns vs. 1000ns) than doing it by calling binary.BigEndian.Uint16.
|
||||||
|
csum += uint32(data[i]) << 8
|
||||||
|
csum += uint32(data[i+1])
|
||||||
|
}
|
||||||
|
if len(data)%2 == 1 {
|
||||||
|
csum += uint32(data[length]) << 8
|
||||||
|
}
|
||||||
|
for csum > 0xffff {
|
||||||
|
csum = (csum >> 16) + (csum & 0xffff)
|
||||||
|
}
|
||||||
|
return ^uint16(csum)
|
||||||
|
}
|
||||||
|
|
||||||
|
// computeChecksum computes a TCP or UDP checksum. headerAndPayload is the
|
||||||
|
// serialized TCP or UDP header plus its payload, with the checksum zero'd
|
||||||
|
// out. headerProtocol is the IP protocol number of the upper-layer header.
|
||||||
|
func (c *tcpipchecksum) computeChecksum(headerAndPayload []byte, headerProtocol IPProtocol) (uint16, error) {
|
||||||
|
if c.pseudoheader == nil {
|
||||||
|
return 0, errors.New("TCP/IP layer 4 checksum cannot be computed without network layer... call SetNetworkLayerForChecksum to set which layer to use")
|
||||||
|
}
|
||||||
|
length := uint32(len(headerAndPayload))
|
||||||
|
csum, err := c.pseudoheader.pseudoheaderChecksum()
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
csum += uint32(headerProtocol)
|
||||||
|
csum += length & 0xffff
|
||||||
|
csum += length >> 16
|
||||||
|
return tcpipChecksum(headerAndPayload, csum), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetNetworkLayerForChecksum tells this layer which network layer is wrapping it.
|
||||||
|
// This is needed for computing the checksum when serializing, since TCP/IP transport
|
||||||
|
// layer checksums depends on fields in the IPv4 or IPv6 layer that contains it.
|
||||||
|
// The passed in layer must be an *IPv4 or *IPv6.
|
||||||
|
func (i *tcpipchecksum) SetNetworkLayerForChecksum(l gopacket.NetworkLayer) error {
|
||||||
|
switch v := l.(type) {
|
||||||
|
case *IPv4:
|
||||||
|
i.pseudoheader = v
|
||||||
|
case *IPv6:
|
||||||
|
i.pseudoheader = v
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("cannot use layer type %v for tcp checksum network layer", l.LayerType())
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,103 @@
|
||||||
|
#!/usr/bin/python
|
||||||
|
# Copyright 2012 Google, Inc. All rights reserved.
|
||||||
|
|
||||||
|
"""TestCreator creates test templates from pcap files."""
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import base64
|
||||||
|
import glob
|
||||||
|
import re
|
||||||
|
import string
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
|
||||||
|
|
||||||
|
class Packet(object):
|
||||||
|
"""Helper class encapsulating packet from a pcap file."""
|
||||||
|
|
||||||
|
def __init__(self, packet_lines):
|
||||||
|
self.packet_lines = packet_lines
|
||||||
|
self.data = self._DecodeText(packet_lines)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _DecodeText(cls, packet_lines):
|
||||||
|
packet_bytes = []
|
||||||
|
# First line is timestamp and stuff, skip it.
|
||||||
|
# Format: 0x0010: 0000 0020 3aff 3ffe 0000 0000 0000 0000 ....:.?.........
|
||||||
|
|
||||||
|
for line in packet_lines[1:]:
|
||||||
|
m = re.match(r'\s+0x[a-f\d]+:\s+((?:[\da-f]{2,4}\s)*)', line, re.IGNORECASE)
|
||||||
|
if m is None: continue
|
||||||
|
for hexpart in m.group(1).split():
|
||||||
|
packet_bytes.append(base64.b16decode(hexpart.upper()))
|
||||||
|
return ''.join(packet_bytes)
|
||||||
|
|
||||||
|
def Test(self, name, link_type):
|
||||||
|
"""Yields a test using this packet, as a set of lines."""
|
||||||
|
yield '// testPacket%s is the packet:' % name
|
||||||
|
for line in self.packet_lines:
|
||||||
|
yield '// ' + line
|
||||||
|
yield 'var testPacket%s = []byte{' % name
|
||||||
|
data = list(self.data)
|
||||||
|
while data:
|
||||||
|
linebytes, data = data[:16], data[16:]
|
||||||
|
yield ''.join(['\t'] + ['0x%02x, ' % ord(c) for c in linebytes])
|
||||||
|
yield '}'
|
||||||
|
yield 'func TestPacket%s(t *testing.T) {' % name
|
||||||
|
yield '\tp := gopacket.NewPacket(testPacket%s, LinkType%s, gopacket.Default)' % (name, link_type)
|
||||||
|
yield '\tif p.ErrorLayer() != nil {'
|
||||||
|
yield '\t\tt.Error("Failed to decode packet:", p.ErrorLayer().Error())'
|
||||||
|
yield '\t}'
|
||||||
|
yield '\tcheckLayers(p, []gopacket.LayerType{LayerType%s, FILL_ME_IN_WITH_ACTUAL_LAYERS}, t)' % link_type
|
||||||
|
yield '}'
|
||||||
|
yield 'func BenchmarkDecodePacket%s(b *testing.B) {' % name
|
||||||
|
yield '\tfor i := 0; i < b.N; i++ {'
|
||||||
|
yield '\t\tgopacket.NewPacket(testPacket%s, LinkType%s, gopacket.NoCopy)' % (name, link_type)
|
||||||
|
yield '\t}'
|
||||||
|
yield '}'
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def GetTcpdumpOutput(filename):
|
||||||
|
"""Runs tcpdump on the given file, returning output as string."""
|
||||||
|
return subprocess.check_output(
|
||||||
|
['tcpdump', '-XX', '-s', '0', '-n', '-r', filename])
|
||||||
|
|
||||||
|
|
||||||
|
def TcpdumpOutputToPackets(output):
|
||||||
|
"""Reads a pcap file with TCPDump, yielding Packet objects."""
|
||||||
|
pdata = []
|
||||||
|
for line in output.splitlines():
|
||||||
|
if line[0] not in string.whitespace and pdata:
|
||||||
|
yield Packet(pdata)
|
||||||
|
pdata = []
|
||||||
|
pdata.append(line)
|
||||||
|
if pdata:
|
||||||
|
yield Packet(pdata)
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
class CustomHelpFormatter(argparse.ArgumentDefaultsHelpFormatter):
|
||||||
|
def _format_usage(self, usage, actions, groups, prefix=None):
|
||||||
|
header =('TestCreator creates gopacket tests using a pcap file.\n\n'
|
||||||
|
'Tests are written to standard out... they can then be \n'
|
||||||
|
'copied into the file of your choice and modified as \n'
|
||||||
|
'you see.\n\n')
|
||||||
|
return header + argparse.ArgumentDefaultsHelpFormatter._format_usage(
|
||||||
|
self, usage, actions, groups, prefix)
|
||||||
|
|
||||||
|
parser = argparse.ArgumentParser(formatter_class=CustomHelpFormatter)
|
||||||
|
parser.add_argument('--link_type', default='Ethernet', help='the link type (default: %(default)s)')
|
||||||
|
parser.add_argument('--name', default='Packet%d', help='the layer type, must have "%d" inside it')
|
||||||
|
parser.add_argument('files', metavar='file.pcap', type=str, nargs='+', help='the files to process')
|
||||||
|
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
for arg in args.files:
|
||||||
|
for path in glob.glob(arg):
|
||||||
|
for i, packet in enumerate(TcpdumpOutputToPackets(GetTcpdumpOutput(path))):
|
||||||
|
print '\n'.join(packet.Test(
|
||||||
|
args.name % i, args.link_type))
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
|
@ -0,0 +1,283 @@
|
||||||
|
// Copyright 2018 The GoPacket Authors. All rights reserved.
|
||||||
|
//
|
||||||
|
// Use of this source code is governed by a BSD-style license
|
||||||
|
// that can be found in the LICENSE file in the root of the source
|
||||||
|
// tree.
|
||||||
|
|
||||||
|
package layers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/binary"
|
||||||
|
"errors"
|
||||||
|
|
||||||
|
"github.com/google/gopacket"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TLSType defines the type of data after the TLS Record
|
||||||
|
type TLSType uint8
|
||||||
|
|
||||||
|
// TLSType known values.
|
||||||
|
const (
|
||||||
|
TLSChangeCipherSpec TLSType = 20
|
||||||
|
TLSAlert TLSType = 21
|
||||||
|
TLSHandshake TLSType = 22
|
||||||
|
TLSApplicationData TLSType = 23
|
||||||
|
TLSUnknown TLSType = 255
|
||||||
|
)
|
||||||
|
|
||||||
|
// String shows the register type nicely formatted
|
||||||
|
func (tt TLSType) String() string {
|
||||||
|
switch tt {
|
||||||
|
default:
|
||||||
|
return "Unknown"
|
||||||
|
case TLSChangeCipherSpec:
|
||||||
|
return "Change Cipher Spec"
|
||||||
|
case TLSAlert:
|
||||||
|
return "Alert"
|
||||||
|
case TLSHandshake:
|
||||||
|
return "Handshake"
|
||||||
|
case TLSApplicationData:
|
||||||
|
return "Application Data"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TLSVersion represents the TLS version in numeric format
|
||||||
|
type TLSVersion uint16
|
||||||
|
|
||||||
|
// Strings shows the TLS version nicely formatted
|
||||||
|
func (tv TLSVersion) String() string {
|
||||||
|
switch tv {
|
||||||
|
default:
|
||||||
|
return "Unknown"
|
||||||
|
case 0x0200:
|
||||||
|
return "SSL 2.0"
|
||||||
|
case 0x0300:
|
||||||
|
return "SSL 3.0"
|
||||||
|
case 0x0301:
|
||||||
|
return "TLS 1.0"
|
||||||
|
case 0x0302:
|
||||||
|
return "TLS 1.1"
|
||||||
|
case 0x0303:
|
||||||
|
return "TLS 1.2"
|
||||||
|
case 0x0304:
|
||||||
|
return "TLS 1.3"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TLS is specified in RFC 5246
|
||||||
|
//
|
||||||
|
// TLS Record Protocol
|
||||||
|
// 0 1 2 3 4 5 6 7 8
|
||||||
|
// +--+--+--+--+--+--+--+--+
|
||||||
|
// | Content Type |
|
||||||
|
// +--+--+--+--+--+--+--+--+
|
||||||
|
// | Version (major) |
|
||||||
|
// +--+--+--+--+--+--+--+--+
|
||||||
|
// | Version (minor) |
|
||||||
|
// +--+--+--+--+--+--+--+--+
|
||||||
|
// | Length |
|
||||||
|
// +--+--+--+--+--+--+--+--+
|
||||||
|
// | Length |
|
||||||
|
// +--+--+--+--+--+--+--+--+
|
||||||
|
|
||||||
|
// TLS is actually a slide of TLSrecord structures
|
||||||
|
type TLS struct {
|
||||||
|
BaseLayer
|
||||||
|
|
||||||
|
// TLS Records
|
||||||
|
ChangeCipherSpec []TLSChangeCipherSpecRecord
|
||||||
|
Handshake []TLSHandshakeRecord
|
||||||
|
AppData []TLSAppDataRecord
|
||||||
|
Alert []TLSAlertRecord
|
||||||
|
}
|
||||||
|
|
||||||
|
// TLSRecordHeader contains all the information that each TLS Record types should have
|
||||||
|
type TLSRecordHeader struct {
|
||||||
|
ContentType TLSType
|
||||||
|
Version TLSVersion
|
||||||
|
Length uint16
|
||||||
|
}
|
||||||
|
|
||||||
|
// LayerType returns gopacket.LayerTypeTLS.
|
||||||
|
func (t *TLS) LayerType() gopacket.LayerType { return LayerTypeTLS }
|
||||||
|
|
||||||
|
// decodeTLS decodes the byte slice into a TLS type. It also
|
||||||
|
// setups the application Layer in PacketBuilder.
|
||||||
|
func decodeTLS(data []byte, p gopacket.PacketBuilder) error {
|
||||||
|
t := &TLS{}
|
||||||
|
err := t.DecodeFromBytes(data, p)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
p.AddLayer(t)
|
||||||
|
p.SetApplicationLayer(t)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DecodeFromBytes decodes the slice into the TLS struct.
|
||||||
|
func (t *TLS) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
|
||||||
|
t.BaseLayer.Contents = data
|
||||||
|
t.BaseLayer.Payload = nil
|
||||||
|
|
||||||
|
t.ChangeCipherSpec = t.ChangeCipherSpec[:0]
|
||||||
|
t.Handshake = t.Handshake[:0]
|
||||||
|
t.AppData = t.AppData[:0]
|
||||||
|
t.Alert = t.Alert[:0]
|
||||||
|
|
||||||
|
return t.decodeTLSRecords(data, df)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *TLS) decodeTLSRecords(data []byte, df gopacket.DecodeFeedback) error {
|
||||||
|
if len(data) < 5 {
|
||||||
|
df.SetTruncated()
|
||||||
|
return errors.New("TLS record too short")
|
||||||
|
}
|
||||||
|
|
||||||
|
// since there are no further layers, the baselayer's content is
|
||||||
|
// pointing to this layer
|
||||||
|
// TODO: Consider removing this
|
||||||
|
t.BaseLayer = BaseLayer{Contents: data[:len(data)]}
|
||||||
|
|
||||||
|
var h TLSRecordHeader
|
||||||
|
h.ContentType = TLSType(data[0])
|
||||||
|
h.Version = TLSVersion(binary.BigEndian.Uint16(data[1:3]))
|
||||||
|
h.Length = binary.BigEndian.Uint16(data[3:5])
|
||||||
|
|
||||||
|
if h.ContentType.String() == "Unknown" {
|
||||||
|
return errors.New("Unknown TLS record type")
|
||||||
|
}
|
||||||
|
|
||||||
|
hl := 5 // header length
|
||||||
|
tl := hl + int(h.Length)
|
||||||
|
if len(data) < tl {
|
||||||
|
df.SetTruncated()
|
||||||
|
return errors.New("TLS packet length mismatch")
|
||||||
|
}
|
||||||
|
|
||||||
|
switch h.ContentType {
|
||||||
|
default:
|
||||||
|
return errors.New("Unknown TLS record type")
|
||||||
|
case TLSChangeCipherSpec:
|
||||||
|
var r TLSChangeCipherSpecRecord
|
||||||
|
e := r.decodeFromBytes(h, data[hl:tl], df)
|
||||||
|
if e != nil {
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
t.ChangeCipherSpec = append(t.ChangeCipherSpec, r)
|
||||||
|
case TLSAlert:
|
||||||
|
var r TLSAlertRecord
|
||||||
|
e := r.decodeFromBytes(h, data[hl:tl], df)
|
||||||
|
if e != nil {
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
t.Alert = append(t.Alert, r)
|
||||||
|
case TLSHandshake:
|
||||||
|
var r TLSHandshakeRecord
|
||||||
|
e := r.decodeFromBytes(h, data[hl:tl], df)
|
||||||
|
if e != nil {
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
t.Handshake = append(t.Handshake, r)
|
||||||
|
case TLSApplicationData:
|
||||||
|
var r TLSAppDataRecord
|
||||||
|
e := r.decodeFromBytes(h, data[hl:tl], df)
|
||||||
|
if e != nil {
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
t.AppData = append(t.AppData, r)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(data) == tl {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return t.decodeTLSRecords(data[tl:len(data)], df)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CanDecode implements gopacket.DecodingLayer.
|
||||||
|
func (t *TLS) CanDecode() gopacket.LayerClass {
|
||||||
|
return LayerTypeTLS
|
||||||
|
}
|
||||||
|
|
||||||
|
// NextLayerType implements gopacket.DecodingLayer.
|
||||||
|
func (t *TLS) NextLayerType() gopacket.LayerType {
|
||||||
|
return gopacket.LayerTypeZero
|
||||||
|
}
|
||||||
|
|
||||||
|
// Payload returns nil, since TLS encrypted payload is inside TLSAppDataRecord
|
||||||
|
func (t *TLS) Payload() []byte {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SerializeTo writes the serialized form of this layer into the
|
||||||
|
// SerializationBuffer, implementing gopacket.SerializableLayer.
|
||||||
|
func (t *TLS) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
|
||||||
|
totalLength := 0
|
||||||
|
for _, record := range t.ChangeCipherSpec {
|
||||||
|
if opts.FixLengths {
|
||||||
|
record.Length = 1
|
||||||
|
}
|
||||||
|
totalLength += 5 + 1 // length of header + record
|
||||||
|
}
|
||||||
|
for range t.Handshake {
|
||||||
|
totalLength += 5
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
for _, record := range t.AppData {
|
||||||
|
if opts.FixLengths {
|
||||||
|
record.Length = uint16(len(record.Payload))
|
||||||
|
}
|
||||||
|
totalLength += 5 + len(record.Payload)
|
||||||
|
}
|
||||||
|
for _, record := range t.Alert {
|
||||||
|
if len(record.EncryptedMsg) == 0 {
|
||||||
|
if opts.FixLengths {
|
||||||
|
record.Length = 2
|
||||||
|
}
|
||||||
|
totalLength += 5 + 2
|
||||||
|
} else {
|
||||||
|
if opts.FixLengths {
|
||||||
|
record.Length = uint16(len(record.EncryptedMsg))
|
||||||
|
}
|
||||||
|
totalLength += 5 + len(record.EncryptedMsg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
data, err := b.PrependBytes(totalLength)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
off := 0
|
||||||
|
for _, record := range t.ChangeCipherSpec {
|
||||||
|
off = encodeHeader(record.TLSRecordHeader, data, off)
|
||||||
|
data[off] = byte(record.Message)
|
||||||
|
off++
|
||||||
|
}
|
||||||
|
for _, record := range t.Handshake {
|
||||||
|
off = encodeHeader(record.TLSRecordHeader, data, off)
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
for _, record := range t.AppData {
|
||||||
|
off = encodeHeader(record.TLSRecordHeader, data, off)
|
||||||
|
copy(data[off:], record.Payload)
|
||||||
|
off += len(record.Payload)
|
||||||
|
}
|
||||||
|
for _, record := range t.Alert {
|
||||||
|
off = encodeHeader(record.TLSRecordHeader, data, off)
|
||||||
|
if len(record.EncryptedMsg) == 0 {
|
||||||
|
data[off] = byte(record.Level)
|
||||||
|
data[off+1] = byte(record.Description)
|
||||||
|
off += 2
|
||||||
|
} else {
|
||||||
|
copy(data[off:], record.EncryptedMsg)
|
||||||
|
off += len(record.EncryptedMsg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func encodeHeader(header TLSRecordHeader, data []byte, offset int) int {
|
||||||
|
data[offset] = byte(header.ContentType)
|
||||||
|
binary.BigEndian.PutUint16(data[offset+1:], uint16(header.Version))
|
||||||
|
binary.BigEndian.PutUint16(data[offset+3:], header.Length)
|
||||||
|
|
||||||
|
return offset + 5
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue