mirror of
https://github.com/xtaci/kcptun.git
synced 2024-04-21 12:32:32 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
94c9cacf4f | ||
|
|
9a5b31b470 | ||
|
|
07ad03fecf | ||
|
|
2225d258cb | ||
|
|
e9316f7be4 | ||
|
|
9ac04cd24d | ||
|
|
76c4d231f2 | ||
|
|
160b68cbc5 | ||
|
|
212504f6c2 | ||
|
|
114cd97025 | ||
|
|
b2be91cdb5 | ||
|
|
7cb5adfc7a | ||
|
|
335b2d35bb | ||
|
|
e3c5c133a6 | ||
|
|
d367ce9e52 | ||
|
|
a1fa76173a | ||
|
|
912a97993e | ||
|
|
706274829f | ||
|
|
4358f5600f | ||
|
|
7cf99098cf | ||
|
|
720050cc39 | ||
|
|
e99088b8d2 | ||
|
|
4b574ecbc1 | ||
|
|
8115fe5db9 | ||
|
|
0dabb34467 | ||
|
|
c290f0b008 | ||
|
|
01e7b9f4c2 | ||
|
|
0da917322c | ||
|
|
23cd29a88e | ||
|
|
3c54e12586 | ||
|
|
c71120e7d4 | ||
|
|
ee29dc4fb1 | ||
|
|
77dc451e83 | ||
|
|
8c2d39753e | ||
|
|
3790c74ca8 | ||
|
|
b07931c582 |
@@ -1,7 +1,5 @@
|
||||
language: go
|
||||
go:
|
||||
- 1.11.x
|
||||
- 1.12.x
|
||||
- 1.13.x
|
||||
before_install:
|
||||
- go get github.com/mattn/goveralls
|
||||
|
||||
+3
-2
@@ -1,4 +1,4 @@
|
||||
FROM golang:alpine as builder
|
||||
FROM golang:1.14.9-alpine3.11 as builder
|
||||
MAINTAINER xtaci <daniel820313@gmail.com>
|
||||
ENV GO111MODULE=on
|
||||
RUN apk update && \
|
||||
@@ -6,7 +6,8 @@ RUN apk update && \
|
||||
apk add git gcc libc-dev linux-headers
|
||||
RUN go get -ldflags "-X main.VERSION=$(date -u +%Y%m%d) -s -w" github.com/xtaci/kcptun/client && go get -ldflags "-X main.VERSION=$(date -u +%Y%m%d) -s -w" github.com/xtaci/kcptun/server
|
||||
|
||||
FROM alpine:3.9
|
||||
FROM alpine:3.11
|
||||
RUN apk add --no-cache iptables
|
||||
COPY --from=builder /go/bin /bin
|
||||
EXPOSE 29900/udp
|
||||
EXPOSE 12948
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
# <img src="logo.png" alt="kcptun" height="54px" />
|
||||
|
||||
[![Release][13]][14] [![Powered][17]][18] [![MIT licensed][11]][12] [![Build Status][3]][4] [![Go Report Card][5]][6] [![Downloads][15]][16] [![Docker][1]][2]
|
||||
[![Release][13]][14] [![Powered][17]][18] [![MIT licensed][11]][12] [![Build Status][3]][4] [![Go Report Card][5]][6] [![Downloads][15]][16] [![Docker][1]][2]
|
||||
|
||||
[1]: https://images.microbadger.com/badges/image/xtaci/kcptun.svg
|
||||
[2]: https://microbadger.com/images/xtaci/kcptun
|
||||
[1]: https://img.shields.io/docker/pulls/xtaci/kcptun
|
||||
[2]: https://hub.docker.com/r/xtaci/kcptun
|
||||
[3]: https://travis-ci.org/xtaci/kcptun.svg?branch=master
|
||||
[4]: https://travis-ci.org/xtaci/kcptun
|
||||
[5]: https://goreportcard.com/badge/github.com/xtaci/kcptun
|
||||
@@ -12,7 +12,7 @@
|
||||
[12]: LICENSE.md
|
||||
[13]: https://img.shields.io/github/v/release/xtaci/kcptun?color=orange
|
||||
[14]: https://github.com/xtaci/kcptun/releases/latest
|
||||
[15]: https://img.shields.io/github/downloads/xtaci/kcptun/total.svg?maxAge=1800&color=red
|
||||
[15]: https://img.shields.io/github/downloads/xtaci/kcptun/total.svg?maxAge=1800&color=orange
|
||||
[16]: https://github.com/xtaci/kcptun/releases
|
||||
[17]: https://img.shields.io/badge/KCP-Powered-blue.svg
|
||||
[18]: https://github.com/skywind3000/kcp
|
||||
@@ -152,7 +152,7 @@ GLOBAL OPTIONS:
|
||||
--mode value profiles: fast3, fast2, fast, normal, manual (default: "fast")
|
||||
--conn value set num of UDP connections to server (default: 1)
|
||||
--autoexpire value set auto expiration time(in seconds) for a single UDP connection, 0 to disable (default: 0)
|
||||
--scavengettl value set how long an expired connection can live(in sec), -1 to disable (default: 600)
|
||||
--scavengettl value set how long an expired connection can live (in seconds) (default: 600)
|
||||
--mtu value set maximum transmission unit for UDP packets (default: 1350)
|
||||
--sndwnd value set send window size(num of packets) (default: 128)
|
||||
--rcvwnd value set receive window size(num of packets) (default: 512)
|
||||
@@ -224,8 +224,6 @@ It is able to detect and correct multiple symbol errors. By adding t check symbo
|
||||
|
||||

|
||||
|
||||
Setting parameters of RS-Code with ```-datashard m -parityshard n``` on **BOTH** KCP Client & KCP Server **MUST** be **IDENTICAL**.
|
||||
|
||||
#### DSCP
|
||||
|
||||
Differentiated services or DiffServ is a computer networking architecture that specifies a simple, scalable and coarse-grained mechanism for classifying and managing network traffic and providing quality of service (QoS) on modern IP networks. DiffServ can, for example, be used to provide low-latency to critical network traffic such as voice or streaming media while providing simple best-effort service to non-critical services such as web traffic or file transfers.
|
||||
@@ -357,13 +355,11 @@ Low-level KCP configuration can be altered by using manual mode like above, make
|
||||
|
||||
### Identical Parmeters
|
||||
|
||||
The parameters below **MUST** be **IDENTICAL** on **BOTH** side:
|
||||
These parameters **MUST** be **IDENTICAL** on **BOTH** side:
|
||||
|
||||
1. -key
|
||||
1. -crypt
|
||||
1. -nocomp
|
||||
1. -datashard
|
||||
1. -parityshard
|
||||
1. -smuxver
|
||||
|
||||
### References
|
||||
|
||||
+18
-12
@@ -35,8 +35,8 @@ for os in ${OSES[@]}; do
|
||||
then
|
||||
suffix=".exe"
|
||||
fi
|
||||
env CGO_ENABLED=0 GOOS=$os GOARCH=amd64 go build -ldflags "$LDFLAGS" -gcflags "$GCFLAGS" -o client_${os}_amd64${suffix} github.com/xtaci/kcptun/client
|
||||
env CGO_ENABLED=0 GOOS=$os GOARCH=amd64 go build -ldflags "$LDFLAGS" -gcflags "$GCFLAGS" -o server_${os}_amd64${suffix} github.com/xtaci/kcptun/server
|
||||
env CGO_ENABLED=0 GOOS=$os GOARCH=amd64 go build -mod=vendor -ldflags "$LDFLAGS" -gcflags "$GCFLAGS" -o client_${os}_amd64${suffix} github.com/xtaci/kcptun/client
|
||||
env CGO_ENABLED=0 GOOS=$os GOARCH=amd64 go build -mod=vendor -ldflags "$LDFLAGS" -gcflags "$GCFLAGS" -o server_${os}_amd64${suffix} github.com/xtaci/kcptun/server
|
||||
if $UPX; then upx -9 client_${os}_amd64${suffix} server_${os}_amd64${suffix};fi
|
||||
tar -zcf kcptun-${os}-amd64-$VERSION.tar.gz client_${os}_amd64${suffix} server_${os}_amd64${suffix}
|
||||
$sum kcptun-${os}-amd64-$VERSION.tar.gz
|
||||
@@ -50,35 +50,41 @@ for os in ${OSES[@]}; do
|
||||
then
|
||||
suffix=".exe"
|
||||
fi
|
||||
env CGO_ENABLED=0 GOOS=$os GOARCH=386 go build -ldflags "$LDFLAGS" -gcflags "$GCFLAGS" -o client_${os}_386${suffix} github.com/xtaci/kcptun/client
|
||||
env CGO_ENABLED=0 GOOS=$os GOARCH=386 go build -ldflags "$LDFLAGS" -gcflags "$GCFLAGS" -o server_${os}_386${suffix} github.com/xtaci/kcptun/server
|
||||
env CGO_ENABLED=0 GOOS=$os GOARCH=386 go build -mod=vendor -ldflags "$LDFLAGS" -gcflags "$GCFLAGS" -o client_${os}_386${suffix} github.com/xtaci/kcptun/client
|
||||
env CGO_ENABLED=0 GOOS=$os GOARCH=386 go build -mod=vendor -ldflags "$LDFLAGS" -gcflags "$GCFLAGS" -o server_${os}_386${suffix} github.com/xtaci/kcptun/server
|
||||
if $UPX; then upx -9 client_${os}_386${suffix} server_${os}_386${suffix};fi
|
||||
tar -zcf kcptun-${os}-386-$VERSION.tar.gz client_${os}_386${suffix} server_${os}_386${suffix}
|
||||
$sum kcptun-${os}-386-$VERSION.tar.gz
|
||||
done
|
||||
|
||||
#Apple M1 device
|
||||
env CGO_ENABLED=0 GOOS=darwin GOARCH=arm64 go build -mod=vendor -ldflags "$LDFLAGS" -gcflags "$GCFLAGS" -o server_darwin_arm64 github.com/xtaci/kcptun/server
|
||||
env CGO_ENABLED=0 GOOS=darwin GOARCH=arm64 go build -mod=vendor -ldflags "$LDFLAGS" -gcflags "$GCFLAGS" -o client_darwin_arm64 github.com/xtaci/kcptun/client
|
||||
tar -zcf kcptun-darwin-arm64-$VERSION.tar.gz client_darwin_arm64 server_darwin_arm64
|
||||
$sum kcptun-darwin-arm64-$VERSION.tar.gz
|
||||
|
||||
# ARM
|
||||
ARMS=(5 6 7)
|
||||
for v in ${ARMS[@]}; do
|
||||
env CGO_ENABLED=0 GOOS=linux GOARCH=arm GOARM=$v go build -ldflags "$LDFLAGS" -gcflags "$GCFLAGS" -o client_linux_arm$v github.com/xtaci/kcptun/client
|
||||
env CGO_ENABLED=0 GOOS=linux GOARCH=arm GOARM=$v go build -ldflags "$LDFLAGS" -gcflags "$GCFLAGS" -o server_linux_arm$v github.com/xtaci/kcptun/server
|
||||
env CGO_ENABLED=0 GOOS=linux GOARCH=arm GOARM=$v go build -mod=vendor -ldflags "$LDFLAGS" -gcflags "$GCFLAGS" -o client_linux_arm$v github.com/xtaci/kcptun/client
|
||||
env CGO_ENABLED=0 GOOS=linux GOARCH=arm GOARM=$v go build -mod=vendor -ldflags "$LDFLAGS" -gcflags "$GCFLAGS" -o server_linux_arm$v github.com/xtaci/kcptun/server
|
||||
if $UPX; then upx -9 client_linux_arm$v server_linux_arm$v;fi
|
||||
tar -zcf kcptun-linux-arm$v-$VERSION.tar.gz client_linux_arm$v server_linux_arm$v
|
||||
$sum kcptun-linux-arm$v-$VERSION.tar.gz
|
||||
done
|
||||
|
||||
# ARM64
|
||||
env CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build -ldflags "$LDFLAGS" -gcflags "$GCFLAGS" -o client_linux_arm64 github.com/xtaci/kcptun/client
|
||||
env CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build -ldflags "$LDFLAGS" -gcflags "$GCFLAGS" -o server_linux_arm64 github.com/xtaci/kcptun/server
|
||||
env CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build -mod=vendor -ldflags "$LDFLAGS" -gcflags "$GCFLAGS" -o client_linux_arm64 github.com/xtaci/kcptun/client
|
||||
env CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build -mod=vendor -ldflags "$LDFLAGS" -gcflags "$GCFLAGS" -o server_linux_arm64 github.com/xtaci/kcptun/server
|
||||
if $UPX; then upx -9 client_linux_arm64 server_linux_arm64*;fi
|
||||
tar -zcf kcptun-linux-arm64-$VERSION.tar.gz client_linux_arm64 server_linux_arm64
|
||||
$sum kcptun-linux-arm64-$VERSION.tar.gz
|
||||
|
||||
#MIPS32LE
|
||||
env CGO_ENABLED=0 GOOS=linux GOARCH=mipsle GOMIPS=softfloat go build -ldflags "$LDFLAGS" -gcflags "$GCFLAGS" -o client_linux_mipsle github.com/xtaci/kcptun/client
|
||||
env CGO_ENABLED=0 GOOS=linux GOARCH=mipsle GOMIPS=softfloat go build -ldflags "$LDFLAGS" -gcflags "$GCFLAGS" -o server_linux_mipsle github.com/xtaci/kcptun/server
|
||||
env CGO_ENABLED=0 GOOS=linux GOARCH=mips GOMIPS=softfloat go build -ldflags "$LDFLAGS" -gcflags "$GCFLAGS" -o client_linux_mips github.com/xtaci/kcptun/client
|
||||
env CGO_ENABLED=0 GOOS=linux GOARCH=mips GOMIPS=softfloat go build -ldflags "$LDFLAGS" -gcflags "$GCFLAGS" -o server_linux_mips github.com/xtaci/kcptun/server
|
||||
env CGO_ENABLED=0 GOOS=linux GOARCH=mipsle GOMIPS=softfloat go build -mod=vendor -ldflags "$LDFLAGS" -gcflags "$GCFLAGS" -o client_linux_mipsle github.com/xtaci/kcptun/client
|
||||
env CGO_ENABLED=0 GOOS=linux GOARCH=mipsle GOMIPS=softfloat go build -mod=vendor -ldflags "$LDFLAGS" -gcflags "$GCFLAGS" -o server_linux_mipsle github.com/xtaci/kcptun/server
|
||||
env CGO_ENABLED=0 GOOS=linux GOARCH=mips GOMIPS=softfloat go build -mod=vendor -ldflags "$LDFLAGS" -gcflags "$GCFLAGS" -o client_linux_mips github.com/xtaci/kcptun/client
|
||||
env CGO_ENABLED=0 GOOS=linux GOARCH=mips GOMIPS=softfloat go build -mod=vendor -ldflags "$LDFLAGS" -gcflags "$GCFLAGS" -o server_linux_mips github.com/xtaci/kcptun/server
|
||||
|
||||
if $UPX; then upx -9 client_linux_mips* server_linux_mips*;fi
|
||||
tar -zcf kcptun-linux-mipsle-$VERSION.tar.gz client_linux_mipsle server_linux_mipsle
|
||||
|
||||
+41
-32
@@ -73,6 +73,11 @@ func checkError(err error) {
|
||||
}
|
||||
}
|
||||
|
||||
type timedSession struct {
|
||||
session *smux.Session
|
||||
expiryDate time.Time
|
||||
}
|
||||
|
||||
func main() {
|
||||
rand.Seed(int64(time.Now().Nanosecond()))
|
||||
if VERSION == "SELFBUILD" {
|
||||
@@ -124,7 +129,7 @@ func main() {
|
||||
cli.IntFlag{
|
||||
Name: "scavengettl",
|
||||
Value: 600,
|
||||
Usage: "set how long an expired connection can live(in sec), -1 to disable",
|
||||
Usage: "set how long an expired connection can live (in seconds)",
|
||||
},
|
||||
cli.IntFlag{
|
||||
Name: "mtu",
|
||||
@@ -421,20 +426,16 @@ func main() {
|
||||
}
|
||||
}
|
||||
|
||||
numconn := uint16(config.Conn)
|
||||
muxes := make([]struct {
|
||||
session *smux.Session
|
||||
ttl time.Time
|
||||
}, numconn)
|
||||
|
||||
for k := range muxes {
|
||||
muxes[k].session = waitConn()
|
||||
muxes[k].ttl = time.Now().Add(time.Duration(config.AutoExpire) * time.Second)
|
||||
}
|
||||
|
||||
chScavenger := make(chan *smux.Session, 128)
|
||||
go scavenger(chScavenger, config.ScavengeTTL)
|
||||
// start snmp logger
|
||||
go generic.SnmpLogger(config.SnmpLog, config.SnmpPeriod)
|
||||
|
||||
// start scavenger
|
||||
chScavenger := make(chan timedSession, 128)
|
||||
go scavenger(chScavenger, &config)
|
||||
|
||||
// start listener
|
||||
numconn := uint16(config.Conn)
|
||||
muxes := make([]timedSession, numconn)
|
||||
rr := uint16(0)
|
||||
for {
|
||||
p1, err := listener.AcceptTCP()
|
||||
@@ -444,10 +445,13 @@ func main() {
|
||||
idx := rr % numconn
|
||||
|
||||
// do auto expiration && reconnection
|
||||
if muxes[idx].session.IsClosed() || (config.AutoExpire > 0 && time.Now().After(muxes[idx].ttl)) {
|
||||
chScavenger <- muxes[idx].session
|
||||
if muxes[idx].session == nil || muxes[idx].session.IsClosed() ||
|
||||
(config.AutoExpire > 0 && time.Now().After(muxes[idx].expiryDate)) {
|
||||
muxes[idx].session = waitConn()
|
||||
muxes[idx].ttl = time.Now().Add(time.Duration(config.AutoExpire) * time.Second)
|
||||
muxes[idx].expiryDate = time.Now().Add(time.Duration(config.AutoExpire) * time.Second)
|
||||
if config.AutoExpire > 0 { // only when autoexpire set
|
||||
chScavenger <- muxes[idx]
|
||||
}
|
||||
}
|
||||
|
||||
go handleClient(muxes[idx].session, p1, config.Quiet)
|
||||
@@ -457,30 +461,35 @@ func main() {
|
||||
myApp.Run(os.Args)
|
||||
}
|
||||
|
||||
type scavengeSession struct {
|
||||
session *smux.Session
|
||||
ts time.Time
|
||||
}
|
||||
func scavenger(ch chan timedSession, config *Config) {
|
||||
// When AutoExpire is set to 0 (default), sessionList will keep empty.
|
||||
// Then this routine won't need to do anything; thus just terminate it.
|
||||
if config.AutoExpire <= 0 {
|
||||
return
|
||||
}
|
||||
|
||||
func scavenger(ch chan *smux.Session, ttl int) {
|
||||
ticker := time.NewTicker(time.Second)
|
||||
defer ticker.Stop()
|
||||
var sessionList []scavengeSession
|
||||
var sessionList []timedSession
|
||||
for {
|
||||
select {
|
||||
case sess := <-ch:
|
||||
sessionList = append(sessionList, scavengeSession{sess, time.Now()})
|
||||
log.Println("session marked as expired", sess.RemoteAddr())
|
||||
case item := <-ch:
|
||||
sessionList = append(sessionList, timedSession{
|
||||
item.session,
|
||||
item.expiryDate.Add(time.Duration(config.ScavengeTTL) * time.Second)})
|
||||
case <-ticker.C:
|
||||
var newList []scavengeSession
|
||||
if len(sessionList) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
var newList []timedSession
|
||||
for k := range sessionList {
|
||||
s := sessionList[k]
|
||||
if s.session.NumStreams() == 0 || s.session.IsClosed() {
|
||||
log.Println("session normally closed", s.session.RemoteAddr())
|
||||
s.session.Close()
|
||||
} else if ttl >= 0 && time.Since(s.ts) >= time.Duration(ttl)*time.Second {
|
||||
log.Println("session reached scavenge ttl", s.session.RemoteAddr())
|
||||
if s.session.IsClosed() {
|
||||
log.Println("scavenger: session normally closed:", s.session.LocalAddr())
|
||||
} else if time.Now().After(s.expiryDate) {
|
||||
s.session.Close()
|
||||
log.Println("scavenger: session closed due to ttl:", s.session.LocalAddr())
|
||||
} else {
|
||||
newList = append(newList, sessionList[k])
|
||||
}
|
||||
|
||||
@@ -4,12 +4,12 @@ require (
|
||||
github.com/coreos/go-iptables v0.4.2 // indirect
|
||||
github.com/golang/snappy v0.0.1
|
||||
github.com/google/gopacket v1.1.17 // indirect
|
||||
github.com/pkg/errors v0.8.1
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/urfave/cli v1.21.0
|
||||
github.com/xtaci/kcp-go/v5 v5.4.26
|
||||
github.com/xtaci/smux v1.5.7
|
||||
github.com/xtaci/kcp-go/v5 v5.6.1
|
||||
github.com/xtaci/smux v1.5.16
|
||||
github.com/xtaci/tcpraw v1.2.25
|
||||
golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413
|
||||
golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de
|
||||
)
|
||||
|
||||
go 1.13
|
||||
go 1.14
|
||||
|
||||
@@ -1,55 +1,89 @@
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/coreos/go-iptables v0.4.2 h1:KH0EwId05JwWIfb96gWvkiT2cbuOu8ygqUaB+yPAwIg=
|
||||
github.com/coreos/go-iptables v0.4.2/go.mod h1:/mVI274lEDI2ns62jHCDnCyBF9Iwsmekav8Dbxlm1MU=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4=
|
||||
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/google/gopacket v1.1.17 h1:rMrlX2ZY2UbvT+sdz3+6J+pp2z+msCq9MxTU6ymxbBY=
|
||||
github.com/google/gopacket v1.1.17/go.mod h1:UdDNZ1OO62aGYVnPhxT1U6aI7ukYtA/kB8vaU0diBUM=
|
||||
github.com/klauspost/cpuid v1.2.2 h1:1xAgYebNnsb9LKCdLOvFWtAxGU/33mjJtyOVbmUa0Us=
|
||||
github.com/klauspost/cpuid v1.2.2/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
|
||||
github.com/klauspost/reedsolomon v1.9.3 h1:N/VzgeMfHmLc+KHMD1UL/tNkfXAt8FnUqlgXGIduwAY=
|
||||
github.com/klauspost/reedsolomon v1.9.3/go.mod h1:CwCi+NUr9pqSVktrkN+Ondf06rkhYZ/pcNv7fu+8Un4=
|
||||
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/klauspost/cpuid v1.2.4/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
|
||||
github.com/klauspost/cpuid v1.3.1 h1:5JNjFYYQrZeKRJ0734q51WCEEn2huer72Dc7K+R/b6s=
|
||||
github.com/klauspost/cpuid v1.3.1/go.mod h1:bYW4mA6ZgKPob1/Dlai2LviZJO7KGI3uoWLd42rAQw4=
|
||||
github.com/klauspost/reedsolomon v1.9.9 h1:qCL7LZlv17xMixl55nq2/Oa1Y86nfO8EqDfv2GHND54=
|
||||
github.com/klauspost/reedsolomon v1.9.9/go.mod h1:O7yFFHiQwDR6b2t63KPUpccPtNdp5ADgh1gg4fd12wo=
|
||||
github.com/mmcloughlin/avo v0.0.0-20200803215136-443f81d77104 h1:ULR/QWMgcgRiZLUjSSJMU+fW+RDMstRdmnDWj9Q+AsA=
|
||||
github.com/mmcloughlin/avo v0.0.0-20200803215136-443f81d77104/go.mod h1:wqKykBG2QzQDJEzvRkcS8x6MiSJkF52hXZsXcjaB3ls=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/templexxx/cpu v0.0.1 h1:hY4WdLOgKdc8y13EYklu9OUTXik80BkxHoWvTO6MQQY=
|
||||
github.com/templexxx/cpu v0.0.1/go.mod h1:w7Tb+7qgcAlIyX4NhLuDKt78AHA5SzPmq0Wj6HiEnnk=
|
||||
github.com/templexxx/cpufeat v0.0.0-20180724012125-cef66df7f161 h1:89CEmDvlq/F7SJEOqkIdNDGJXrQIhuIx9D2DBXjavSU=
|
||||
github.com/templexxx/cpufeat v0.0.0-20180724012125-cef66df7f161/go.mod h1:wM7WEvslTq+iOEAMDLSzhVuOt5BRZ05WirO+b09GHQU=
|
||||
github.com/templexxx/xor v0.0.0-20191217153810-f85b25db303b h1:fj5tQ8acgNUr6O8LEplsxDhUIe2573iLkJc+PqnzZTI=
|
||||
github.com/templexxx/xor v0.0.0-20191217153810-f85b25db303b/go.mod h1:5XA7W9S6mni3h5uvOC75dA3m9CCCaS83lltmc0ukdi4=
|
||||
github.com/templexxx/xorsimd v0.3.1 h1:K6aBXKOSPgs0Pz/VXDy0pQK/RlwvtI1Cf9sOm3dPDrU=
|
||||
github.com/templexxx/xorsimd v0.3.1/go.mod h1:W+ffZz8jJMH2SXwuKu9WhygqBMbFnp14G2fqEr8qaNo=
|
||||
github.com/templexxx/cpu v0.0.7 h1:pUEZn8JBy/w5yzdYWgx+0m0xL9uk6j4K91C5kOViAzo=
|
||||
github.com/templexxx/cpu v0.0.7/go.mod h1:w7Tb+7qgcAlIyX4NhLuDKt78AHA5SzPmq0Wj6HiEnnk=
|
||||
github.com/templexxx/xorsimd v0.4.1 h1:iUZcywbOYDRAZUasAs2eSCUW8eobuZDy0I9FJiORkVg=
|
||||
github.com/templexxx/xorsimd v0.4.1/go.mod h1:W+ffZz8jJMH2SXwuKu9WhygqBMbFnp14G2fqEr8qaNo=
|
||||
github.com/tjfoc/gmsm v1.0.1 h1:R11HlqhXkDospckjZEihx9SW/2VW0RgdwrykyWMFOQU=
|
||||
github.com/tjfoc/gmsm v1.0.1/go.mod h1:XxO4hdhhrzAd+G4CjDqaOkd0hUzmtPR/d3EiBBMn/wc=
|
||||
github.com/tjfoc/gmsm v1.3.2 h1:7JVkAn5bvUJ7HtU08iW6UiD+UTmJTIToHCfeFzkcCxM=
|
||||
github.com/tjfoc/gmsm v1.3.2/go.mod h1:HaUcFuY0auTiaHB9MHFGCPx5IaLhTUd2atbCFBQXn9w=
|
||||
github.com/urfave/cli v1.21.0 h1:wYSSj06510qPIzGSua9ZqsncMmWE3Zr55KBERygyrxE=
|
||||
github.com/urfave/cli v1.21.0/go.mod h1:lxDj6qX9Q6lWQxIrbrT0nwecwUtRnhVZAJjJZrVUZZQ=
|
||||
github.com/xtaci/kcp-go v5.4.20+incompatible h1:TN1uey3Raw0sTz0Fg8GkfM0uH3YwzhnZWQ1bABv5xAg=
|
||||
github.com/xtaci/kcp-go v5.4.20+incompatible/go.mod h1:bN6vIwHQbfHaHtFpEssmWsN45a+AZwO7eyRCmEIbtvE=
|
||||
github.com/xtaci/kcp-go/v5 v5.4.23 h1:fQG2rrEYWRKyq/PRtUZdBZ+RSr3S/3QQFuScA/KzjxU=
|
||||
github.com/xtaci/kcp-go/v5 v5.4.23/go.mod h1:kaRUj0x4HWAdtA32VaDNGGY8AZTRsFIOSCJQOWYcDc0=
|
||||
github.com/xtaci/kcp-go/v5 v5.4.25 h1:hD2bpx3R92TXvXoYGQ844heHekXNmXafnpKUE/uxGbc=
|
||||
github.com/xtaci/kcp-go/v5 v5.4.25/go.mod h1:kaRUj0x4HWAdtA32VaDNGGY8AZTRsFIOSCJQOWYcDc0=
|
||||
github.com/xtaci/kcp-go/v5 v5.4.26 h1:4NhV2D9c8IMUzhxI8eS0QVRR4MPqjhxoPGoh7Za3lQU=
|
||||
github.com/xtaci/kcp-go/v5 v5.4.26/go.mod h1:Oyw+zrBrO58urX1AaWV+2RynthEKcs+qrRAh0Q8YpdU=
|
||||
github.com/xtaci/kcp-go/v5 v5.5.16-0.20201009084714-dc0fc2364c92 h1:a9X2tFWnJR0JjYSz2n4PdmTGtDHtlnU7VI5bNjUDsyI=
|
||||
github.com/xtaci/kcp-go/v5 v5.5.16-0.20201009084714-dc0fc2364c92/go.mod h1:W3kVPyNYwZ06p79dNwFWQOVFrdcBpDBsdyvK8moQrYo=
|
||||
github.com/xtaci/kcp-go/v5 v5.5.16-0.20201009115342-ce66a98f9547 h1:SFJWLwOnjTJCt7JKf2fYH0RWtWJTkvYKIEfLvjJfOqM=
|
||||
github.com/xtaci/kcp-go/v5 v5.5.16-0.20201009115342-ce66a98f9547/go.mod h1:W3kVPyNYwZ06p79dNwFWQOVFrdcBpDBsdyvK8moQrYo=
|
||||
github.com/xtaci/kcp-go/v5 v5.5.17 h1:bkdaqtER0PMlP05BBHfu6W+71kt/NwbAk93KH7F78Ck=
|
||||
github.com/xtaci/kcp-go/v5 v5.5.17/go.mod h1:pVx3jb4LT5edTmPayc77tIU9nRsjGck8wep5ZV/RBO0=
|
||||
github.com/xtaci/kcp-go/v5 v5.6.1 h1:Pwn0aoeNSPF9dTS7IgiPXn0HEtaIlVb6y5UKWPsx8bI=
|
||||
github.com/xtaci/kcp-go/v5 v5.6.1/go.mod h1:W3kVPyNYwZ06p79dNwFWQOVFrdcBpDBsdyvK8moQrYo=
|
||||
github.com/xtaci/lossyconn v0.0.0-20190602105132-8df528c0c9ae h1:J0GxkO96kL4WF+AIT3M4mfUVinOCPgf2uUWYFUzN0sM=
|
||||
github.com/xtaci/lossyconn v0.0.0-20190602105132-8df528c0c9ae/go.mod h1:gXtu8J62kEgmN++bm9BVICuT/e8yiLI2KFobd/TRFsE=
|
||||
github.com/xtaci/smux v1.5.7 h1:fVOjux34i112nzp5BIF7yg4WvVKLj1pUMqEgSdiBho4=
|
||||
github.com/xtaci/smux v1.5.7/go.mod h1:OMlQbT5vcgl2gb49mFkYo6SMf+zP3rcjcwQz7ZU7IGY=
|
||||
github.com/xtaci/smux v1.5.14 h1:1j+zJYDZRv9FHaWqCJfH5RPizIm0fSzJIFbfVn8zsfg=
|
||||
github.com/xtaci/smux v1.5.14/go.mod h1:OMlQbT5vcgl2gb49mFkYo6SMf+zP3rcjcwQz7ZU7IGY=
|
||||
github.com/xtaci/smux v1.5.15 h1:6hMiXswcleXj5oNfcJc+DXS8Vj36XX2LaX98udog6Kc=
|
||||
github.com/xtaci/smux v1.5.15/go.mod h1:OMlQbT5vcgl2gb49mFkYo6SMf+zP3rcjcwQz7ZU7IGY=
|
||||
github.com/xtaci/smux v1.5.16 h1:FBPYOkW8ZTjLKUM4LI4xnnuuDC8CQ/dB04HD519WoEk=
|
||||
github.com/xtaci/smux v1.5.16/go.mod h1:OMlQbT5vcgl2gb49mFkYo6SMf+zP3rcjcwQz7ZU7IGY=
|
||||
github.com/xtaci/tcpraw v1.2.25 h1:VDlqo0op17JeXBM6e2G9ocCNLOJcw9mZbobMbJjo0vk=
|
||||
github.com/xtaci/tcpraw v1.2.25/go.mod h1:dKyZ2V75s0cZ7cbgJYdxPvms7af0joIeOyx1GgJQbLk=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
golang.org/x/arch v0.0.0-20190909030613-46d78d1859ac/go.mod h1:flIaEI6LNU6xOCD5PaJvn9wGP0agmIOqjrtsKGRguv4=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413 h1:ULYEB3JvPRE/IfO+9uO7vKV/xzVTO7XPAwm8xbf4w2g=
|
||||
golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20191219195013-becbf705a915/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de h1:ikNHVSjEfnvz6sxdSPCaPt572qowuyMDMJLLm3Db3ig=
|
||||
golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553 h1:efeOvDhwQ29Dj3SdAV/MJf8oukgn+8D8WgaCaRMchF8=
|
||||
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20200707034311-ab3426394381 h1:VXak5I6aEWmAXeQjA+QSZzlgNrpq9mjcfDemuexIKsU=
|
||||
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190405154228-4b34438f7a67/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8 h1:JA8d3MPx/IToSyXZG/RhwYEtfrKO1Fxrqe8KrkiLXKM=
|
||||
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200808120158-1030fc2bf1d9 h1:yi1hN8dcqI9l8klZfy4B8mJvFmmAxJEePIQQFNSd7Cs=
|
||||
golang.org/x/sys v0.0.0-20200808120158-1030fc2bf1d9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20200425043458-8463f397d07c/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200808161706-5bf02b21f123 h1:4JSJPND/+4555t1HfXYF4UEqDqiSKCgeV0+hbA8hMs4=
|
||||
golang.org/x/tools v0.0.0-20200808161706-5bf02b21f123/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
|
||||
|
||||
+191
@@ -0,0 +1,191 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction, and
|
||||
distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by the copyright
|
||||
owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all other entities
|
||||
that control, are controlled by, or are under common control with that entity.
|
||||
For the purposes of this definition, "control" means (i) the power, direct or
|
||||
indirect, to cause the direction or management of such entity, whether by
|
||||
contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity exercising
|
||||
permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications, including
|
||||
but not limited to software source code, documentation source, and configuration
|
||||
files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical transformation or
|
||||
translation of a Source form, including but not limited to compiled object code,
|
||||
generated documentation, and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or Object form, made
|
||||
available under the License, as indicated by a copyright notice that is included
|
||||
in or attached to the work (an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object form, that
|
||||
is based on (or derived from) the Work and for which the editorial revisions,
|
||||
annotations, elaborations, or other modifications represent, as a whole, an
|
||||
original work of authorship. For the purposes of this License, Derivative Works
|
||||
shall not include works that remain separable from, or merely link (or bind by
|
||||
name) to the interfaces of, the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including the original version
|
||||
of the Work and any modifications or additions to that Work or Derivative Works
|
||||
thereof, that is intentionally submitted to Licensor for inclusion in the Work
|
||||
by the copyright owner or by an individual or Legal Entity authorized to submit
|
||||
on behalf of the copyright owner. For the purposes of this definition,
|
||||
"submitted" means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems, and
|
||||
issue tracking systems that are managed by, or on behalf of, the Licensor for
|
||||
the purpose of discussing and improving the Work, but excluding communication
|
||||
that is conspicuously marked or otherwise designated in writing by the copyright
|
||||
owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity on behalf
|
||||
of whom a Contribution has been received by Licensor and subsequently
|
||||
incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License.
|
||||
|
||||
Subject to the terms and conditions of this License, each Contributor hereby
|
||||
grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
|
||||
irrevocable copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the Work and such
|
||||
Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License.
|
||||
|
||||
Subject to the terms and conditions of this License, each Contributor hereby
|
||||
grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
|
||||
irrevocable (except as stated in this section) patent license to make, have
|
||||
made, use, offer to sell, sell, import, and otherwise transfer the Work, where
|
||||
such license applies only to those patent claims licensable by such Contributor
|
||||
that are necessarily infringed by their Contribution(s) alone or by combination
|
||||
of their Contribution(s) with the Work to which such Contribution(s) was
|
||||
submitted. If You institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work or a
|
||||
Contribution incorporated within the Work constitutes direct or contributory
|
||||
patent infringement, then any patent licenses granted to You under this License
|
||||
for that Work shall terminate as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution.
|
||||
|
||||
You may reproduce and distribute copies of the Work or Derivative Works thereof
|
||||
in any medium, with or without modifications, and in Source or Object form,
|
||||
provided that You meet the following conditions:
|
||||
|
||||
You must give any other recipients of the Work or Derivative Works a copy of
|
||||
this License; and
|
||||
You must cause any modified files to carry prominent notices stating that You
|
||||
changed the files; and
|
||||
You must retain, in the Source form of any Derivative Works that You distribute,
|
||||
all copyright, patent, trademark, and attribution notices from the Source form
|
||||
of the Work, excluding those notices that do not pertain to any part of the
|
||||
Derivative Works; and
|
||||
If the Work includes a "NOTICE" text file as part of its distribution, then any
|
||||
Derivative Works that You distribute must include a readable copy of the
|
||||
attribution notices contained within such NOTICE file, excluding those notices
|
||||
that do not pertain to any part of the Derivative Works, in at least one of the
|
||||
following places: within a NOTICE text file distributed as part of the
|
||||
Derivative Works; within the Source form or documentation, if provided along
|
||||
with the Derivative Works; or, within a display generated by the Derivative
|
||||
Works, if and wherever such third-party notices normally appear. The contents of
|
||||
the NOTICE file are for informational purposes only and do not modify the
|
||||
License. You may add Your own attribution notices within Derivative Works that
|
||||
You distribute, alongside or as an addendum to the NOTICE text from the Work,
|
||||
provided that such additional attribution notices cannot be construed as
|
||||
modifying the License.
|
||||
You may add Your own copyright statement to Your modifications and may provide
|
||||
additional or different license terms and conditions for use, reproduction, or
|
||||
distribution of Your modifications, or for any such Derivative Works as a whole,
|
||||
provided Your use, reproduction, and distribution of the Work otherwise complies
|
||||
with the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions.
|
||||
|
||||
Unless You explicitly state otherwise, any Contribution intentionally submitted
|
||||
for inclusion in the Work by You to the Licensor shall be under the terms and
|
||||
conditions of this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify the terms of
|
||||
any separate license agreement you may have executed with Licensor regarding
|
||||
such Contributions.
|
||||
|
||||
6. Trademarks.
|
||||
|
||||
This License does not grant permission to use the trade names, trademarks,
|
||||
service marks, or product names of the Licensor, except as required for
|
||||
reasonable and customary use in describing the origin of the Work and
|
||||
reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty.
|
||||
|
||||
Unless required by applicable law or agreed to in writing, Licensor provides the
|
||||
Work (and each Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied,
|
||||
including, without limitation, any warranties or conditions of TITLE,
|
||||
NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are
|
||||
solely responsible for determining the appropriateness of using or
|
||||
redistributing the Work and assume any risks associated with Your exercise of
|
||||
permissions under this License.
|
||||
|
||||
8. Limitation of Liability.
|
||||
|
||||
In no event and under no legal theory, whether in tort (including negligence),
|
||||
contract, or otherwise, unless required by applicable law (such as deliberate
|
||||
and grossly negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special, incidental,
|
||||
or consequential damages of any character arising as a result of this License or
|
||||
out of the use or inability to use the Work (including but not limited to
|
||||
damages for loss of goodwill, work stoppage, computer failure or malfunction, or
|
||||
any and all other commercial damages or losses), even if such Contributor has
|
||||
been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability.
|
||||
|
||||
While redistributing the Work or Derivative Works thereof, You may choose to
|
||||
offer, and charge a fee for, acceptance of support, warranty, indemnity, or
|
||||
other liability obligations and/or rights consistent with this License. However,
|
||||
in accepting such obligations, You may act only on Your own behalf and on Your
|
||||
sole responsibility, not on behalf of any other Contributor, and only if You
|
||||
agree to indemnify, defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason of your
|
||||
accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work
|
||||
|
||||
To apply the Apache License to your work, attach the following boilerplate
|
||||
notice, with the fields enclosed by brackets "[]" replaced with your own
|
||||
identifying information. (Don't include the brackets!) The text should be
|
||||
enclosed in the appropriate comment syntax for the file format. We also
|
||||
recommend that a file or class name and description of purpose be included on
|
||||
the same "printed page" as the copyright notice for easier identification within
|
||||
third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
CoreOS Project
|
||||
Copyright 2018 CoreOS, Inc
|
||||
|
||||
This product includes software developed at CoreOS, Inc.
|
||||
(http://www.coreos.com/).
|
||||
+598
@@ -0,0 +1,598 @@
|
||||
// Copyright 2015 CoreOS, Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package iptables
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"os/exec"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
// Adds the output of stderr to exec.ExitError
|
||||
type Error struct {
|
||||
exec.ExitError
|
||||
cmd exec.Cmd
|
||||
msg string
|
||||
proto Protocol
|
||||
exitStatus *int //for overriding
|
||||
}
|
||||
|
||||
func (e *Error) ExitStatus() int {
|
||||
if e.exitStatus != nil {
|
||||
return *e.exitStatus
|
||||
}
|
||||
return e.Sys().(syscall.WaitStatus).ExitStatus()
|
||||
}
|
||||
|
||||
func (e *Error) Error() string {
|
||||
return fmt.Sprintf("running %v: exit status %v: %v", e.cmd.Args, e.ExitStatus(), e.msg)
|
||||
}
|
||||
|
||||
// IsNotExist returns true if the error is due to the chain or rule not existing
|
||||
func (e *Error) IsNotExist() bool {
|
||||
return e.ExitStatus() == 1 &&
|
||||
(e.msg == fmt.Sprintf("%s: Bad rule (does a matching rule exist in that chain?).\n", getIptablesCommand(e.proto)) ||
|
||||
e.msg == fmt.Sprintf("%s: No chain/target/match by that name.\n", getIptablesCommand(e.proto)))
|
||||
}
|
||||
|
||||
// Protocol to differentiate between IPv4 and IPv6
|
||||
type Protocol byte
|
||||
|
||||
const (
|
||||
ProtocolIPv4 Protocol = iota
|
||||
ProtocolIPv6
|
||||
)
|
||||
|
||||
type IPTables struct {
|
||||
path string
|
||||
proto Protocol
|
||||
hasCheck bool
|
||||
hasWait bool
|
||||
hasRandomFully bool
|
||||
v1 int
|
||||
v2 int
|
||||
v3 int
|
||||
mode string // the underlying iptables operating mode, e.g. nf_tables
|
||||
}
|
||||
|
||||
// Stat represents a structured statistic entry.
|
||||
type Stat struct {
|
||||
Packets uint64 `json:"pkts"`
|
||||
Bytes uint64 `json:"bytes"`
|
||||
Target string `json:"target"`
|
||||
Protocol string `json:"prot"`
|
||||
Opt string `json:"opt"`
|
||||
Input string `json:"in"`
|
||||
Output string `json:"out"`
|
||||
Source *net.IPNet `json:"source"`
|
||||
Destination *net.IPNet `json:"destination"`
|
||||
Options string `json:"options"`
|
||||
}
|
||||
|
||||
// New creates a new IPTables.
|
||||
// For backwards compatibility, this always uses IPv4, i.e. "iptables".
|
||||
func New() (*IPTables, error) {
|
||||
return NewWithProtocol(ProtocolIPv4)
|
||||
}
|
||||
|
||||
// New creates a new IPTables for the given proto.
|
||||
// The proto will determine which command is used, either "iptables" or "ip6tables".
|
||||
func NewWithProtocol(proto Protocol) (*IPTables, error) {
|
||||
path, err := exec.LookPath(getIptablesCommand(proto))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
vstring, err := getIptablesVersionString(path)
|
||||
v1, v2, v3, mode, err := extractIptablesVersion(vstring)
|
||||
|
||||
checkPresent, waitPresent, randomFullyPresent := getIptablesCommandSupport(v1, v2, v3)
|
||||
|
||||
ipt := IPTables{
|
||||
path: path,
|
||||
proto: proto,
|
||||
hasCheck: checkPresent,
|
||||
hasWait: waitPresent,
|
||||
hasRandomFully: randomFullyPresent,
|
||||
v1: v1,
|
||||
v2: v2,
|
||||
v3: v3,
|
||||
mode: mode,
|
||||
}
|
||||
return &ipt, nil
|
||||
}
|
||||
|
||||
// Proto returns the protocol used by this IPTables.
|
||||
func (ipt *IPTables) Proto() Protocol {
|
||||
return ipt.proto
|
||||
}
|
||||
|
||||
// Exists checks if given rulespec in specified table/chain exists
|
||||
func (ipt *IPTables) Exists(table, chain string, rulespec ...string) (bool, error) {
|
||||
if !ipt.hasCheck {
|
||||
return ipt.existsForOldIptables(table, chain, rulespec)
|
||||
|
||||
}
|
||||
cmd := append([]string{"-t", table, "-C", chain}, rulespec...)
|
||||
err := ipt.run(cmd...)
|
||||
eerr, eok := err.(*Error)
|
||||
switch {
|
||||
case err == nil:
|
||||
return true, nil
|
||||
case eok && eerr.ExitStatus() == 1:
|
||||
return false, nil
|
||||
default:
|
||||
return false, err
|
||||
}
|
||||
}
|
||||
|
||||
// Insert inserts rulespec to specified table/chain (in specified pos)
|
||||
func (ipt *IPTables) Insert(table, chain string, pos int, rulespec ...string) error {
|
||||
cmd := append([]string{"-t", table, "-I", chain, strconv.Itoa(pos)}, rulespec...)
|
||||
return ipt.run(cmd...)
|
||||
}
|
||||
|
||||
// Append appends rulespec to specified table/chain
|
||||
func (ipt *IPTables) Append(table, chain string, rulespec ...string) error {
|
||||
cmd := append([]string{"-t", table, "-A", chain}, rulespec...)
|
||||
return ipt.run(cmd...)
|
||||
}
|
||||
|
||||
// AppendUnique acts like Append except that it won't add a duplicate
|
||||
func (ipt *IPTables) AppendUnique(table, chain string, rulespec ...string) error {
|
||||
exists, err := ipt.Exists(table, chain, rulespec...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !exists {
|
||||
return ipt.Append(table, chain, rulespec...)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Delete removes rulespec in specified table/chain
|
||||
func (ipt *IPTables) Delete(table, chain string, rulespec ...string) error {
|
||||
cmd := append([]string{"-t", table, "-D", chain}, rulespec...)
|
||||
return ipt.run(cmd...)
|
||||
}
|
||||
|
||||
// List rules in specified table/chain
|
||||
func (ipt *IPTables) List(table, chain string) ([]string, error) {
|
||||
args := []string{"-t", table, "-S", chain}
|
||||
return ipt.executeList(args)
|
||||
}
|
||||
|
||||
// List rules (with counters) in specified table/chain
|
||||
func (ipt *IPTables) ListWithCounters(table, chain string) ([]string, error) {
|
||||
args := []string{"-t", table, "-v", "-S", chain}
|
||||
return ipt.executeList(args)
|
||||
}
|
||||
|
||||
// ListChains returns a slice containing the name of each chain in the specified table.
|
||||
func (ipt *IPTables) ListChains(table string) ([]string, error) {
|
||||
args := []string{"-t", table, "-S"}
|
||||
|
||||
result, err := ipt.executeList(args)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Iterate over rules to find all default (-P) and user-specified (-N) chains.
|
||||
// Chains definition always come before rules.
|
||||
// Format is the following:
|
||||
// -P OUTPUT ACCEPT
|
||||
// -N Custom
|
||||
var chains []string
|
||||
for _, val := range result {
|
||||
if strings.HasPrefix(val, "-P") || strings.HasPrefix(val, "-N") {
|
||||
chains = append(chains, strings.Fields(val)[1])
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
return chains, nil
|
||||
}
|
||||
|
||||
// Stats lists rules including the byte and packet counts
|
||||
func (ipt *IPTables) Stats(table, chain string) ([][]string, error) {
|
||||
args := []string{"-t", table, "-L", chain, "-n", "-v", "-x"}
|
||||
lines, err := ipt.executeList(args)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
appendSubnet := func(addr string) string {
|
||||
if strings.IndexByte(addr, byte('/')) < 0 {
|
||||
if strings.IndexByte(addr, '.') < 0 {
|
||||
return addr + "/128"
|
||||
}
|
||||
return addr + "/32"
|
||||
}
|
||||
return addr
|
||||
}
|
||||
|
||||
ipv6 := ipt.proto == ProtocolIPv6
|
||||
|
||||
rows := [][]string{}
|
||||
for i, line := range lines {
|
||||
// Skip over chain name and field header
|
||||
if i < 2 {
|
||||
continue
|
||||
}
|
||||
|
||||
// Fields:
|
||||
// 0=pkts 1=bytes 2=target 3=prot 4=opt 5=in 6=out 7=source 8=destination 9=options
|
||||
line = strings.TrimSpace(line)
|
||||
fields := strings.Fields(line)
|
||||
|
||||
// The ip6tables verbose output cannot be naively split due to the default "opt"
|
||||
// field containing 2 single spaces.
|
||||
if ipv6 {
|
||||
// Check if field 6 is "opt" or "source" address
|
||||
dest := fields[6]
|
||||
ip, _, _ := net.ParseCIDR(dest)
|
||||
if ip == nil {
|
||||
ip = net.ParseIP(dest)
|
||||
}
|
||||
|
||||
// If we detected a CIDR or IP, the "opt" field is empty.. insert it.
|
||||
if ip != nil {
|
||||
f := []string{}
|
||||
f = append(f, fields[:4]...)
|
||||
f = append(f, " ") // Empty "opt" field for ip6tables
|
||||
f = append(f, fields[4:]...)
|
||||
fields = f
|
||||
}
|
||||
}
|
||||
|
||||
// Adjust "source" and "destination" to include netmask, to match regular
|
||||
// List output
|
||||
fields[7] = appendSubnet(fields[7])
|
||||
fields[8] = appendSubnet(fields[8])
|
||||
|
||||
// Combine "options" fields 9... into a single space-delimited field.
|
||||
options := fields[9:]
|
||||
fields = fields[:9]
|
||||
fields = append(fields, strings.Join(options, " "))
|
||||
rows = append(rows, fields)
|
||||
}
|
||||
return rows, nil
|
||||
}
|
||||
|
||||
// ParseStat parses a single statistic row into a Stat struct. The input should
|
||||
// be a string slice that is returned from calling the Stat method.
|
||||
func (ipt *IPTables) ParseStat(stat []string) (parsed Stat, err error) {
|
||||
// For forward-compatibility, expect at least 10 fields in the stat
|
||||
if len(stat) < 10 {
|
||||
return parsed, fmt.Errorf("stat contained fewer fields than expected")
|
||||
}
|
||||
|
||||
// Convert the fields that are not plain strings
|
||||
parsed.Packets, err = strconv.ParseUint(stat[0], 0, 64)
|
||||
if err != nil {
|
||||
return parsed, fmt.Errorf(err.Error(), "could not parse packets")
|
||||
}
|
||||
parsed.Bytes, err = strconv.ParseUint(stat[1], 0, 64)
|
||||
if err != nil {
|
||||
return parsed, fmt.Errorf(err.Error(), "could not parse bytes")
|
||||
}
|
||||
_, parsed.Source, err = net.ParseCIDR(stat[7])
|
||||
if err != nil {
|
||||
return parsed, fmt.Errorf(err.Error(), "could not parse source")
|
||||
}
|
||||
_, parsed.Destination, err = net.ParseCIDR(stat[8])
|
||||
if err != nil {
|
||||
return parsed, fmt.Errorf(err.Error(), "could not parse destination")
|
||||
}
|
||||
|
||||
// Put the fields that are strings
|
||||
parsed.Target = stat[2]
|
||||
parsed.Protocol = stat[3]
|
||||
parsed.Opt = stat[4]
|
||||
parsed.Input = stat[5]
|
||||
parsed.Output = stat[6]
|
||||
parsed.Options = stat[9]
|
||||
|
||||
return parsed, nil
|
||||
}
|
||||
|
||||
// StructuredStats returns statistics as structured data which may be further
|
||||
// parsed and marshaled.
|
||||
func (ipt *IPTables) StructuredStats(table, chain string) ([]Stat, error) {
|
||||
rawStats, err := ipt.Stats(table, chain)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
structStats := []Stat{}
|
||||
for _, rawStat := range rawStats {
|
||||
stat, err := ipt.ParseStat(rawStat)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
structStats = append(structStats, stat)
|
||||
}
|
||||
|
||||
return structStats, nil
|
||||
}
|
||||
|
||||
func (ipt *IPTables) executeList(args []string) ([]string, error) {
|
||||
var stdout bytes.Buffer
|
||||
if err := ipt.runWithOutput(args, &stdout); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rules := strings.Split(stdout.String(), "\n")
|
||||
|
||||
// strip trailing newline
|
||||
if len(rules) > 0 && rules[len(rules)-1] == "" {
|
||||
rules = rules[:len(rules)-1]
|
||||
}
|
||||
|
||||
// nftables mode doesn't return an error code when listing a non-existent
|
||||
// chain. Patch that up.
|
||||
if len(rules) == 0 && ipt.mode == "nf_tables" {
|
||||
v := 1
|
||||
return nil, &Error{
|
||||
cmd: exec.Cmd{Args: args},
|
||||
msg: fmt.Sprintf("%s: No chain/target/match by that name.\n", getIptablesCommand(ipt.proto)),
|
||||
proto: ipt.proto,
|
||||
exitStatus: &v,
|
||||
}
|
||||
}
|
||||
|
||||
for i, rule := range rules {
|
||||
rules[i] = filterRuleOutput(rule)
|
||||
}
|
||||
|
||||
return rules, nil
|
||||
}
|
||||
|
||||
// NewChain creates a new chain in the specified table.
|
||||
// If the chain already exists, it will result in an error.
|
||||
func (ipt *IPTables) NewChain(table, chain string) error {
|
||||
return ipt.run("-t", table, "-N", chain)
|
||||
}
|
||||
|
||||
const existsErr = 1
|
||||
|
||||
// ClearChain flushed (deletes all rules) in the specified table/chain.
|
||||
// If the chain does not exist, a new one will be created
|
||||
func (ipt *IPTables) ClearChain(table, chain string) error {
|
||||
err := ipt.NewChain(table, chain)
|
||||
|
||||
eerr, eok := err.(*Error)
|
||||
switch {
|
||||
case err == nil:
|
||||
return nil
|
||||
case eok && eerr.ExitStatus() == existsErr:
|
||||
// chain already exists. Flush (clear) it.
|
||||
return ipt.run("-t", table, "-F", chain)
|
||||
default:
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// RenameChain renames the old chain to the new one.
|
||||
func (ipt *IPTables) RenameChain(table, oldChain, newChain string) error {
|
||||
return ipt.run("-t", table, "-E", oldChain, newChain)
|
||||
}
|
||||
|
||||
// DeleteChain deletes the chain in the specified table.
|
||||
// The chain must be empty
|
||||
func (ipt *IPTables) DeleteChain(table, chain string) error {
|
||||
return ipt.run("-t", table, "-X", chain)
|
||||
}
|
||||
|
||||
// ChangePolicy changes policy on chain to target
|
||||
func (ipt *IPTables) ChangePolicy(table, chain, target string) error {
|
||||
return ipt.run("-t", table, "-P", chain, target)
|
||||
}
|
||||
|
||||
// Check if the underlying iptables command supports the --random-fully flag
|
||||
func (ipt *IPTables) HasRandomFully() bool {
|
||||
return ipt.hasRandomFully
|
||||
}
|
||||
|
||||
// Return version components of the underlying iptables command
|
||||
func (ipt *IPTables) GetIptablesVersion() (int, int, int) {
|
||||
return ipt.v1, ipt.v2, ipt.v3
|
||||
}
|
||||
|
||||
// run runs an iptables command with the given arguments, ignoring
|
||||
// any stdout output
|
||||
func (ipt *IPTables) run(args ...string) error {
|
||||
return ipt.runWithOutput(args, nil)
|
||||
}
|
||||
|
||||
// runWithOutput runs an iptables command with the given arguments,
|
||||
// writing any stdout output to the given writer
|
||||
func (ipt *IPTables) runWithOutput(args []string, stdout io.Writer) error {
|
||||
args = append([]string{ipt.path}, args...)
|
||||
if ipt.hasWait {
|
||||
args = append(args, "--wait")
|
||||
} else {
|
||||
fmu, err := newXtablesFileLock()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ul, err := fmu.tryLock()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer ul.Unlock()
|
||||
}
|
||||
|
||||
var stderr bytes.Buffer
|
||||
cmd := exec.Cmd{
|
||||
Path: ipt.path,
|
||||
Args: args,
|
||||
Stdout: stdout,
|
||||
Stderr: &stderr,
|
||||
}
|
||||
|
||||
if err := cmd.Run(); err != nil {
|
||||
switch e := err.(type) {
|
||||
case *exec.ExitError:
|
||||
return &Error{*e, cmd, stderr.String(), ipt.proto, nil}
|
||||
default:
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// getIptablesCommand returns the correct command for the given protocol, either "iptables" or "ip6tables".
|
||||
func getIptablesCommand(proto Protocol) string {
|
||||
if proto == ProtocolIPv6 {
|
||||
return "ip6tables"
|
||||
} else {
|
||||
return "iptables"
|
||||
}
|
||||
}
|
||||
|
||||
// Checks if iptables has the "-C" and "--wait" flag
|
||||
func getIptablesCommandSupport(v1 int, v2 int, v3 int) (bool, bool, bool) {
|
||||
return iptablesHasCheckCommand(v1, v2, v3), iptablesHasWaitCommand(v1, v2, v3), iptablesHasRandomFully(v1, v2, v3)
|
||||
}
|
||||
|
||||
// getIptablesVersion returns the first three components of the iptables version
|
||||
// and the operating mode (e.g. nf_tables or legacy)
|
||||
// e.g. "iptables v1.3.66" would return (1, 3, 66, legacy, nil)
|
||||
func extractIptablesVersion(str string) (int, int, int, string, error) {
|
||||
versionMatcher := regexp.MustCompile(`v([0-9]+)\.([0-9]+)\.([0-9]+)(?:\s+\((\w+))?`)
|
||||
result := versionMatcher.FindStringSubmatch(str)
|
||||
if result == nil {
|
||||
return 0, 0, 0, "", fmt.Errorf("no iptables version found in string: %s", str)
|
||||
}
|
||||
|
||||
v1, err := strconv.Atoi(result[1])
|
||||
if err != nil {
|
||||
return 0, 0, 0, "", err
|
||||
}
|
||||
|
||||
v2, err := strconv.Atoi(result[2])
|
||||
if err != nil {
|
||||
return 0, 0, 0, "", err
|
||||
}
|
||||
|
||||
v3, err := strconv.Atoi(result[3])
|
||||
if err != nil {
|
||||
return 0, 0, 0, "", err
|
||||
}
|
||||
|
||||
mode := "legacy"
|
||||
if result[4] != "" {
|
||||
mode = result[4]
|
||||
}
|
||||
return v1, v2, v3, mode, nil
|
||||
}
|
||||
|
||||
// Runs "iptables --version" to get the version string
|
||||
func getIptablesVersionString(path string) (string, error) {
|
||||
cmd := exec.Command(path, "--version")
|
||||
var out bytes.Buffer
|
||||
cmd.Stdout = &out
|
||||
err := cmd.Run()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return out.String(), nil
|
||||
}
|
||||
|
||||
// Checks if an iptables version is after 1.4.11, when --check was added
|
||||
func iptablesHasCheckCommand(v1 int, v2 int, v3 int) bool {
|
||||
if v1 > 1 {
|
||||
return true
|
||||
}
|
||||
if v1 == 1 && v2 > 4 {
|
||||
return true
|
||||
}
|
||||
if v1 == 1 && v2 == 4 && v3 >= 11 {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Checks if an iptables version is after 1.4.20, when --wait was added
|
||||
func iptablesHasWaitCommand(v1 int, v2 int, v3 int) bool {
|
||||
if v1 > 1 {
|
||||
return true
|
||||
}
|
||||
if v1 == 1 && v2 > 4 {
|
||||
return true
|
||||
}
|
||||
if v1 == 1 && v2 == 4 && v3 >= 20 {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Checks if an iptables version is after 1.6.2, when --random-fully was added
|
||||
func iptablesHasRandomFully(v1 int, v2 int, v3 int) bool {
|
||||
if v1 > 1 {
|
||||
return true
|
||||
}
|
||||
if v1 == 1 && v2 > 6 {
|
||||
return true
|
||||
}
|
||||
if v1 == 1 && v2 == 6 && v3 >= 2 {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Checks if a rule specification exists for a table
|
||||
func (ipt *IPTables) existsForOldIptables(table, chain string, rulespec []string) (bool, error) {
|
||||
rs := strings.Join(append([]string{"-A", chain}, rulespec...), " ")
|
||||
args := []string{"-t", table, "-S"}
|
||||
var stdout bytes.Buffer
|
||||
err := ipt.runWithOutput(args, &stdout)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return strings.Contains(stdout.String(), rs), nil
|
||||
}
|
||||
|
||||
// counterRegex is the regex used to detect nftables counter format
|
||||
var counterRegex = regexp.MustCompile(`^\[([0-9]+):([0-9]+)\] `)
|
||||
|
||||
// filterRuleOutput works around some inconsistencies in output.
|
||||
// For example, when iptables is in legacy vs. nftables mode, it produces
|
||||
// different results.
|
||||
func filterRuleOutput(rule string) string {
|
||||
out := rule
|
||||
|
||||
// work around an output difference in nftables mode where counters
|
||||
// are output in iptables-save format, rather than iptables -S format
|
||||
// The string begins with "[0:0]"
|
||||
//
|
||||
// Fixes #49
|
||||
if groups := counterRegex.FindStringSubmatch(out); groups != nil {
|
||||
// drop the brackets
|
||||
out = out[len(groups[0]):]
|
||||
out = fmt.Sprintf("%s -c %s %s", out, groups[1], groups[2])
|
||||
}
|
||||
|
||||
return out
|
||||
}
|
||||
+84
@@ -0,0 +1,84 @@
|
||||
// Copyright 2015 CoreOS, Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package iptables
|
||||
|
||||
import (
|
||||
"os"
|
||||
"sync"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
const (
|
||||
// In earlier versions of iptables, the xtables lock was implemented
|
||||
// via a Unix socket, but now flock is used via this lockfile:
|
||||
// http://git.netfilter.org/iptables/commit/?id=aa562a660d1555b13cffbac1e744033e91f82707
|
||||
// Note the LSB-conforming "/run" directory does not exist on old
|
||||
// distributions, so assume "/var" is symlinked
|
||||
xtablesLockFilePath = "/var/run/xtables.lock"
|
||||
|
||||
defaultFilePerm = 0600
|
||||
)
|
||||
|
||||
type Unlocker interface {
|
||||
Unlock() error
|
||||
}
|
||||
|
||||
type nopUnlocker struct{}
|
||||
|
||||
func (_ nopUnlocker) Unlock() error { return nil }
|
||||
|
||||
type fileLock struct {
|
||||
// mu is used to protect against concurrent invocations from within this process
|
||||
mu sync.Mutex
|
||||
fd int
|
||||
}
|
||||
|
||||
// tryLock takes an exclusive lock on the xtables lock file without blocking.
|
||||
// This is best-effort only: if the exclusive lock would block (i.e. because
|
||||
// another process already holds it), no error is returned. Otherwise, any
|
||||
// error encountered during the locking operation is returned.
|
||||
// The returned Unlocker should be used to release the lock when the caller is
|
||||
// done invoking iptables commands.
|
||||
func (l *fileLock) tryLock() (Unlocker, error) {
|
||||
l.mu.Lock()
|
||||
err := syscall.Flock(l.fd, syscall.LOCK_EX|syscall.LOCK_NB)
|
||||
switch err {
|
||||
case syscall.EWOULDBLOCK:
|
||||
l.mu.Unlock()
|
||||
return nopUnlocker{}, nil
|
||||
case nil:
|
||||
return l, nil
|
||||
default:
|
||||
l.mu.Unlock()
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// Unlock closes the underlying file, which implicitly unlocks it as well. It
|
||||
// also unlocks the associated mutex.
|
||||
func (l *fileLock) Unlock() error {
|
||||
defer l.mu.Unlock()
|
||||
return syscall.Close(l.fd)
|
||||
}
|
||||
|
||||
// newXtablesFileLock opens a new lock on the xtables lockfile without
|
||||
// acquiring the lock
|
||||
func newXtablesFileLock() (*fileLock, error) {
|
||||
fd, err := syscall.Open(xtablesLockFilePath, os.O_CREATE, defaultFilePerm)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &fileLock{fd: fd}, nil
|
||||
}
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
cmd/snappytool/snappytool
|
||||
testdata/bench
|
||||
|
||||
# These explicitly listed benchmark data files are for an obsolete version of
|
||||
# snappy_test.go.
|
||||
testdata/alice29.txt
|
||||
testdata/asyoulik.txt
|
||||
testdata/fireworks.jpeg
|
||||
testdata/geo.protodata
|
||||
testdata/html
|
||||
testdata/html_x_4
|
||||
testdata/kppkn.gtb
|
||||
testdata/lcet10.txt
|
||||
testdata/paper-100k.pdf
|
||||
testdata/plrabn12.txt
|
||||
testdata/urls.10K
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
# This is the official list of Snappy-Go authors for copyright purposes.
|
||||
# This file is distinct from the CONTRIBUTORS files.
|
||||
# See the latter for an explanation.
|
||||
|
||||
# Names should be added to this file as
|
||||
# Name or Organization <email address>
|
||||
# The email address is not required for organizations.
|
||||
|
||||
# Please keep the list sorted.
|
||||
|
||||
Damian Gryski <dgryski@gmail.com>
|
||||
Google Inc.
|
||||
Jan Mercl <0xjnml@gmail.com>
|
||||
Rodolfo Carvalho <rhcarvalho@gmail.com>
|
||||
Sebastien Binet <seb.binet@gmail.com>
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
# This is the official list of people who can contribute
|
||||
# (and typically have contributed) code to the Snappy-Go repository.
|
||||
# The AUTHORS file lists the copyright holders; this file
|
||||
# lists people. For example, Google employees are listed here
|
||||
# but not in AUTHORS, because Google holds the copyright.
|
||||
#
|
||||
# The submission process automatically checks to make sure
|
||||
# that people submitting code are listed in this file (by email address).
|
||||
#
|
||||
# Names should be added to this file only after verifying that
|
||||
# the individual or the individual's organization has agreed to
|
||||
# the appropriate Contributor License Agreement, found here:
|
||||
#
|
||||
# http://code.google.com/legal/individual-cla-v1.0.html
|
||||
# http://code.google.com/legal/corporate-cla-v1.0.html
|
||||
#
|
||||
# The agreement for individuals can be filled out on the web.
|
||||
#
|
||||
# When adding J Random Contributor's name to this file,
|
||||
# either J's name or J's organization's name should be
|
||||
# added to the AUTHORS file, depending on whether the
|
||||
# individual or corporate CLA was used.
|
||||
|
||||
# Names should be added to this file like so:
|
||||
# Name <email address>
|
||||
|
||||
# Please keep the list sorted.
|
||||
|
||||
Damian Gryski <dgryski@gmail.com>
|
||||
Jan Mercl <0xjnml@gmail.com>
|
||||
Kai Backman <kaib@golang.org>
|
||||
Marc-Antoine Ruel <maruel@chromium.org>
|
||||
Nigel Tao <nigeltao@golang.org>
|
||||
Rob Pike <r@golang.org>
|
||||
Rodolfo Carvalho <rhcarvalho@gmail.com>
|
||||
Russ Cox <rsc@golang.org>
|
||||
Sebastien Binet <seb.binet@gmail.com>
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
Copyright (c) 2011 The Snappy-Go Authors. 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 Google Inc. 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.
|
||||
+107
@@ -0,0 +1,107 @@
|
||||
The Snappy compression format in the Go programming language.
|
||||
|
||||
To download and install from source:
|
||||
$ go get github.com/golang/snappy
|
||||
|
||||
Unless otherwise noted, the Snappy-Go source files are distributed
|
||||
under the BSD-style license found in the LICENSE file.
|
||||
|
||||
|
||||
|
||||
Benchmarks.
|
||||
|
||||
The golang/snappy benchmarks include compressing (Z) and decompressing (U) ten
|
||||
or so files, the same set used by the C++ Snappy code (github.com/google/snappy
|
||||
and note the "google", not "golang"). On an "Intel(R) Core(TM) i7-3770 CPU @
|
||||
3.40GHz", Go's GOARCH=amd64 numbers as of 2016-05-29:
|
||||
|
||||
"go test -test.bench=."
|
||||
|
||||
_UFlat0-8 2.19GB/s ± 0% html
|
||||
_UFlat1-8 1.41GB/s ± 0% urls
|
||||
_UFlat2-8 23.5GB/s ± 2% jpg
|
||||
_UFlat3-8 1.91GB/s ± 0% jpg_200
|
||||
_UFlat4-8 14.0GB/s ± 1% pdf
|
||||
_UFlat5-8 1.97GB/s ± 0% html4
|
||||
_UFlat6-8 814MB/s ± 0% txt1
|
||||
_UFlat7-8 785MB/s ± 0% txt2
|
||||
_UFlat8-8 857MB/s ± 0% txt3
|
||||
_UFlat9-8 719MB/s ± 1% txt4
|
||||
_UFlat10-8 2.84GB/s ± 0% pb
|
||||
_UFlat11-8 1.05GB/s ± 0% gaviota
|
||||
|
||||
_ZFlat0-8 1.04GB/s ± 0% html
|
||||
_ZFlat1-8 534MB/s ± 0% urls
|
||||
_ZFlat2-8 15.7GB/s ± 1% jpg
|
||||
_ZFlat3-8 740MB/s ± 3% jpg_200
|
||||
_ZFlat4-8 9.20GB/s ± 1% pdf
|
||||
_ZFlat5-8 991MB/s ± 0% html4
|
||||
_ZFlat6-8 379MB/s ± 0% txt1
|
||||
_ZFlat7-8 352MB/s ± 0% txt2
|
||||
_ZFlat8-8 396MB/s ± 1% txt3
|
||||
_ZFlat9-8 327MB/s ± 1% txt4
|
||||
_ZFlat10-8 1.33GB/s ± 1% pb
|
||||
_ZFlat11-8 605MB/s ± 1% gaviota
|
||||
|
||||
|
||||
|
||||
"go test -test.bench=. -tags=noasm"
|
||||
|
||||
_UFlat0-8 621MB/s ± 2% html
|
||||
_UFlat1-8 494MB/s ± 1% urls
|
||||
_UFlat2-8 23.2GB/s ± 1% jpg
|
||||
_UFlat3-8 1.12GB/s ± 1% jpg_200
|
||||
_UFlat4-8 4.35GB/s ± 1% pdf
|
||||
_UFlat5-8 609MB/s ± 0% html4
|
||||
_UFlat6-8 296MB/s ± 0% txt1
|
||||
_UFlat7-8 288MB/s ± 0% txt2
|
||||
_UFlat8-8 309MB/s ± 1% txt3
|
||||
_UFlat9-8 280MB/s ± 1% txt4
|
||||
_UFlat10-8 753MB/s ± 0% pb
|
||||
_UFlat11-8 400MB/s ± 0% gaviota
|
||||
|
||||
_ZFlat0-8 409MB/s ± 1% html
|
||||
_ZFlat1-8 250MB/s ± 1% urls
|
||||
_ZFlat2-8 12.3GB/s ± 1% jpg
|
||||
_ZFlat3-8 132MB/s ± 0% jpg_200
|
||||
_ZFlat4-8 2.92GB/s ± 0% pdf
|
||||
_ZFlat5-8 405MB/s ± 1% html4
|
||||
_ZFlat6-8 179MB/s ± 1% txt1
|
||||
_ZFlat7-8 170MB/s ± 1% txt2
|
||||
_ZFlat8-8 189MB/s ± 1% txt3
|
||||
_ZFlat9-8 164MB/s ± 1% txt4
|
||||
_ZFlat10-8 479MB/s ± 1% pb
|
||||
_ZFlat11-8 270MB/s ± 1% gaviota
|
||||
|
||||
|
||||
|
||||
For comparison (Go's encoded output is byte-for-byte identical to C++'s), here
|
||||
are the numbers from C++ Snappy's
|
||||
|
||||
make CXXFLAGS="-O2 -DNDEBUG -g" clean snappy_unittest.log && cat snappy_unittest.log
|
||||
|
||||
BM_UFlat/0 2.4GB/s html
|
||||
BM_UFlat/1 1.4GB/s urls
|
||||
BM_UFlat/2 21.8GB/s jpg
|
||||
BM_UFlat/3 1.5GB/s jpg_200
|
||||
BM_UFlat/4 13.3GB/s pdf
|
||||
BM_UFlat/5 2.1GB/s html4
|
||||
BM_UFlat/6 1.0GB/s txt1
|
||||
BM_UFlat/7 959.4MB/s txt2
|
||||
BM_UFlat/8 1.0GB/s txt3
|
||||
BM_UFlat/9 864.5MB/s txt4
|
||||
BM_UFlat/10 2.9GB/s pb
|
||||
BM_UFlat/11 1.2GB/s gaviota
|
||||
|
||||
BM_ZFlat/0 944.3MB/s html (22.31 %)
|
||||
BM_ZFlat/1 501.6MB/s urls (47.78 %)
|
||||
BM_ZFlat/2 14.3GB/s jpg (99.95 %)
|
||||
BM_ZFlat/3 538.3MB/s jpg_200 (73.00 %)
|
||||
BM_ZFlat/4 8.3GB/s pdf (83.30 %)
|
||||
BM_ZFlat/5 903.5MB/s html4 (22.52 %)
|
||||
BM_ZFlat/6 336.0MB/s txt1 (57.88 %)
|
||||
BM_ZFlat/7 312.3MB/s txt2 (61.91 %)
|
||||
BM_ZFlat/8 353.1MB/s txt3 (54.99 %)
|
||||
BM_ZFlat/9 289.9MB/s txt4 (66.26 %)
|
||||
BM_ZFlat/10 1.2GB/s pb (19.68 %)
|
||||
BM_ZFlat/11 527.4MB/s gaviota (37.72 %)
|
||||
+237
@@ -0,0 +1,237 @@
|
||||
// Copyright 2011 The Snappy-Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package snappy
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"io"
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrCorrupt reports that the input is invalid.
|
||||
ErrCorrupt = errors.New("snappy: corrupt input")
|
||||
// ErrTooLarge reports that the uncompressed length is too large.
|
||||
ErrTooLarge = errors.New("snappy: decoded block is too large")
|
||||
// ErrUnsupported reports that the input isn't supported.
|
||||
ErrUnsupported = errors.New("snappy: unsupported input")
|
||||
|
||||
errUnsupportedLiteralLength = errors.New("snappy: unsupported literal length")
|
||||
)
|
||||
|
||||
// DecodedLen returns the length of the decoded block.
|
||||
func DecodedLen(src []byte) (int, error) {
|
||||
v, _, err := decodedLen(src)
|
||||
return v, err
|
||||
}
|
||||
|
||||
// decodedLen returns the length of the decoded block and the number of bytes
|
||||
// that the length header occupied.
|
||||
func decodedLen(src []byte) (blockLen, headerLen int, err error) {
|
||||
v, n := binary.Uvarint(src)
|
||||
if n <= 0 || v > 0xffffffff {
|
||||
return 0, 0, ErrCorrupt
|
||||
}
|
||||
|
||||
const wordSize = 32 << (^uint(0) >> 32 & 1)
|
||||
if wordSize == 32 && v > 0x7fffffff {
|
||||
return 0, 0, ErrTooLarge
|
||||
}
|
||||
return int(v), n, nil
|
||||
}
|
||||
|
||||
const (
|
||||
decodeErrCodeCorrupt = 1
|
||||
decodeErrCodeUnsupportedLiteralLength = 2
|
||||
)
|
||||
|
||||
// Decode returns the decoded form of src. The returned slice may be a sub-
|
||||
// slice of dst if dst was large enough to hold the entire decoded block.
|
||||
// Otherwise, a newly allocated slice will be returned.
|
||||
//
|
||||
// The dst and src must not overlap. It is valid to pass a nil dst.
|
||||
func Decode(dst, src []byte) ([]byte, error) {
|
||||
dLen, s, err := decodedLen(src)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if dLen <= len(dst) {
|
||||
dst = dst[:dLen]
|
||||
} else {
|
||||
dst = make([]byte, dLen)
|
||||
}
|
||||
switch decode(dst, src[s:]) {
|
||||
case 0:
|
||||
return dst, nil
|
||||
case decodeErrCodeUnsupportedLiteralLength:
|
||||
return nil, errUnsupportedLiteralLength
|
||||
}
|
||||
return nil, ErrCorrupt
|
||||
}
|
||||
|
||||
// NewReader returns a new Reader that decompresses from r, using the framing
|
||||
// format described at
|
||||
// https://github.com/google/snappy/blob/master/framing_format.txt
|
||||
func NewReader(r io.Reader) *Reader {
|
||||
return &Reader{
|
||||
r: r,
|
||||
decoded: make([]byte, maxBlockSize),
|
||||
buf: make([]byte, maxEncodedLenOfMaxBlockSize+checksumSize),
|
||||
}
|
||||
}
|
||||
|
||||
// Reader is an io.Reader that can read Snappy-compressed bytes.
|
||||
type Reader struct {
|
||||
r io.Reader
|
||||
err error
|
||||
decoded []byte
|
||||
buf []byte
|
||||
// decoded[i:j] contains decoded bytes that have not yet been passed on.
|
||||
i, j int
|
||||
readHeader bool
|
||||
}
|
||||
|
||||
// Reset discards any buffered data, resets all state, and switches the Snappy
|
||||
// reader to read from r. This permits reusing a Reader rather than allocating
|
||||
// a new one.
|
||||
func (r *Reader) Reset(reader io.Reader) {
|
||||
r.r = reader
|
||||
r.err = nil
|
||||
r.i = 0
|
||||
r.j = 0
|
||||
r.readHeader = false
|
||||
}
|
||||
|
||||
func (r *Reader) readFull(p []byte, allowEOF bool) (ok bool) {
|
||||
if _, r.err = io.ReadFull(r.r, p); r.err != nil {
|
||||
if r.err == io.ErrUnexpectedEOF || (r.err == io.EOF && !allowEOF) {
|
||||
r.err = ErrCorrupt
|
||||
}
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Read satisfies the io.Reader interface.
|
||||
func (r *Reader) Read(p []byte) (int, error) {
|
||||
if r.err != nil {
|
||||
return 0, r.err
|
||||
}
|
||||
for {
|
||||
if r.i < r.j {
|
||||
n := copy(p, r.decoded[r.i:r.j])
|
||||
r.i += n
|
||||
return n, nil
|
||||
}
|
||||
if !r.readFull(r.buf[:4], true) {
|
||||
return 0, r.err
|
||||
}
|
||||
chunkType := r.buf[0]
|
||||
if !r.readHeader {
|
||||
if chunkType != chunkTypeStreamIdentifier {
|
||||
r.err = ErrCorrupt
|
||||
return 0, r.err
|
||||
}
|
||||
r.readHeader = true
|
||||
}
|
||||
chunkLen := int(r.buf[1]) | int(r.buf[2])<<8 | int(r.buf[3])<<16
|
||||
if chunkLen > len(r.buf) {
|
||||
r.err = ErrUnsupported
|
||||
return 0, r.err
|
||||
}
|
||||
|
||||
// The chunk types are specified at
|
||||
// https://github.com/google/snappy/blob/master/framing_format.txt
|
||||
switch chunkType {
|
||||
case chunkTypeCompressedData:
|
||||
// Section 4.2. Compressed data (chunk type 0x00).
|
||||
if chunkLen < checksumSize {
|
||||
r.err = ErrCorrupt
|
||||
return 0, r.err
|
||||
}
|
||||
buf := r.buf[:chunkLen]
|
||||
if !r.readFull(buf, false) {
|
||||
return 0, r.err
|
||||
}
|
||||
checksum := uint32(buf[0]) | uint32(buf[1])<<8 | uint32(buf[2])<<16 | uint32(buf[3])<<24
|
||||
buf = buf[checksumSize:]
|
||||
|
||||
n, err := DecodedLen(buf)
|
||||
if err != nil {
|
||||
r.err = err
|
||||
return 0, r.err
|
||||
}
|
||||
if n > len(r.decoded) {
|
||||
r.err = ErrCorrupt
|
||||
return 0, r.err
|
||||
}
|
||||
if _, err := Decode(r.decoded, buf); err != nil {
|
||||
r.err = err
|
||||
return 0, r.err
|
||||
}
|
||||
if crc(r.decoded[:n]) != checksum {
|
||||
r.err = ErrCorrupt
|
||||
return 0, r.err
|
||||
}
|
||||
r.i, r.j = 0, n
|
||||
continue
|
||||
|
||||
case chunkTypeUncompressedData:
|
||||
// Section 4.3. Uncompressed data (chunk type 0x01).
|
||||
if chunkLen < checksumSize {
|
||||
r.err = ErrCorrupt
|
||||
return 0, r.err
|
||||
}
|
||||
buf := r.buf[:checksumSize]
|
||||
if !r.readFull(buf, false) {
|
||||
return 0, r.err
|
||||
}
|
||||
checksum := uint32(buf[0]) | uint32(buf[1])<<8 | uint32(buf[2])<<16 | uint32(buf[3])<<24
|
||||
// Read directly into r.decoded instead of via r.buf.
|
||||
n := chunkLen - checksumSize
|
||||
if n > len(r.decoded) {
|
||||
r.err = ErrCorrupt
|
||||
return 0, r.err
|
||||
}
|
||||
if !r.readFull(r.decoded[:n], false) {
|
||||
return 0, r.err
|
||||
}
|
||||
if crc(r.decoded[:n]) != checksum {
|
||||
r.err = ErrCorrupt
|
||||
return 0, r.err
|
||||
}
|
||||
r.i, r.j = 0, n
|
||||
continue
|
||||
|
||||
case chunkTypeStreamIdentifier:
|
||||
// Section 4.1. Stream identifier (chunk type 0xff).
|
||||
if chunkLen != len(magicBody) {
|
||||
r.err = ErrCorrupt
|
||||
return 0, r.err
|
||||
}
|
||||
if !r.readFull(r.buf[:len(magicBody)], false) {
|
||||
return 0, r.err
|
||||
}
|
||||
for i := 0; i < len(magicBody); i++ {
|
||||
if r.buf[i] != magicBody[i] {
|
||||
r.err = ErrCorrupt
|
||||
return 0, r.err
|
||||
}
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if chunkType <= 0x7f {
|
||||
// Section 4.5. Reserved unskippable chunks (chunk types 0x02-0x7f).
|
||||
r.err = ErrUnsupported
|
||||
return 0, r.err
|
||||
}
|
||||
// Section 4.4 Padding (chunk type 0xfe).
|
||||
// Section 4.6. Reserved skippable chunks (chunk types 0x80-0xfd).
|
||||
if !r.readFull(r.buf[:chunkLen], false) {
|
||||
return 0, r.err
|
||||
}
|
||||
}
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
// Copyright 2016 The Snappy-Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build !appengine
|
||||
// +build gc
|
||||
// +build !noasm
|
||||
|
||||
package snappy
|
||||
|
||||
// decode has the same semantics as in decode_other.go.
|
||||
//
|
||||
//go:noescape
|
||||
func decode(dst, src []byte) int
|
||||
+490
@@ -0,0 +1,490 @@
|
||||
// Copyright 2016 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build !appengine
|
||||
// +build gc
|
||||
// +build !noasm
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
// The asm code generally follows the pure Go code in decode_other.go, except
|
||||
// where marked with a "!!!".
|
||||
|
||||
// func decode(dst, src []byte) int
|
||||
//
|
||||
// All local variables fit into registers. The non-zero stack size is only to
|
||||
// spill registers and push args when issuing a CALL. The register allocation:
|
||||
// - AX scratch
|
||||
// - BX scratch
|
||||
// - CX length or x
|
||||
// - DX offset
|
||||
// - SI &src[s]
|
||||
// - DI &dst[d]
|
||||
// + R8 dst_base
|
||||
// + R9 dst_len
|
||||
// + R10 dst_base + dst_len
|
||||
// + R11 src_base
|
||||
// + R12 src_len
|
||||
// + R13 src_base + src_len
|
||||
// - R14 used by doCopy
|
||||
// - R15 used by doCopy
|
||||
//
|
||||
// The registers R8-R13 (marked with a "+") are set at the start of the
|
||||
// function, and after a CALL returns, and are not otherwise modified.
|
||||
//
|
||||
// The d variable is implicitly DI - R8, and len(dst)-d is R10 - DI.
|
||||
// The s variable is implicitly SI - R11, and len(src)-s is R13 - SI.
|
||||
TEXT ·decode(SB), NOSPLIT, $48-56
|
||||
// Initialize SI, DI and R8-R13.
|
||||
MOVQ dst_base+0(FP), R8
|
||||
MOVQ dst_len+8(FP), R9
|
||||
MOVQ R8, DI
|
||||
MOVQ R8, R10
|
||||
ADDQ R9, R10
|
||||
MOVQ src_base+24(FP), R11
|
||||
MOVQ src_len+32(FP), R12
|
||||
MOVQ R11, SI
|
||||
MOVQ R11, R13
|
||||
ADDQ R12, R13
|
||||
|
||||
loop:
|
||||
// for s < len(src)
|
||||
CMPQ SI, R13
|
||||
JEQ end
|
||||
|
||||
// CX = uint32(src[s])
|
||||
//
|
||||
// switch src[s] & 0x03
|
||||
MOVBLZX (SI), CX
|
||||
MOVL CX, BX
|
||||
ANDL $3, BX
|
||||
CMPL BX, $1
|
||||
JAE tagCopy
|
||||
|
||||
// ----------------------------------------
|
||||
// The code below handles literal tags.
|
||||
|
||||
// case tagLiteral:
|
||||
// x := uint32(src[s] >> 2)
|
||||
// switch
|
||||
SHRL $2, CX
|
||||
CMPL CX, $60
|
||||
JAE tagLit60Plus
|
||||
|
||||
// case x < 60:
|
||||
// s++
|
||||
INCQ SI
|
||||
|
||||
doLit:
|
||||
// This is the end of the inner "switch", when we have a literal tag.
|
||||
//
|
||||
// We assume that CX == x and x fits in a uint32, where x is the variable
|
||||
// used in the pure Go decode_other.go code.
|
||||
|
||||
// length = int(x) + 1
|
||||
//
|
||||
// Unlike the pure Go code, we don't need to check if length <= 0 because
|
||||
// CX can hold 64 bits, so the increment cannot overflow.
|
||||
INCQ CX
|
||||
|
||||
// Prepare to check if copying length bytes will run past the end of dst or
|
||||
// src.
|
||||
//
|
||||
// AX = len(dst) - d
|
||||
// BX = len(src) - s
|
||||
MOVQ R10, AX
|
||||
SUBQ DI, AX
|
||||
MOVQ R13, BX
|
||||
SUBQ SI, BX
|
||||
|
||||
// !!! Try a faster technique for short (16 or fewer bytes) copies.
|
||||
//
|
||||
// if length > 16 || len(dst)-d < 16 || len(src)-s < 16 {
|
||||
// goto callMemmove // Fall back on calling runtime·memmove.
|
||||
// }
|
||||
//
|
||||
// The C++ snappy code calls this TryFastAppend. It also checks len(src)-s
|
||||
// against 21 instead of 16, because it cannot assume that all of its input
|
||||
// is contiguous in memory and so it needs to leave enough source bytes to
|
||||
// read the next tag without refilling buffers, but Go's Decode assumes
|
||||
// contiguousness (the src argument is a []byte).
|
||||
CMPQ CX, $16
|
||||
JGT callMemmove
|
||||
CMPQ AX, $16
|
||||
JLT callMemmove
|
||||
CMPQ BX, $16
|
||||
JLT callMemmove
|
||||
|
||||
// !!! Implement the copy from src to dst as a 16-byte load and store.
|
||||
// (Decode's documentation says that dst and src must not overlap.)
|
||||
//
|
||||
// This always copies 16 bytes, instead of only length bytes, but that's
|
||||
// OK. If the input is a valid Snappy encoding then subsequent iterations
|
||||
// will fix up the overrun. Otherwise, Decode returns a nil []byte (and a
|
||||
// non-nil error), so the overrun will be ignored.
|
||||
//
|
||||
// Note that on amd64, it is legal and cheap to issue unaligned 8-byte or
|
||||
// 16-byte loads and stores. This technique probably wouldn't be as
|
||||
// effective on architectures that are fussier about alignment.
|
||||
MOVOU 0(SI), X0
|
||||
MOVOU X0, 0(DI)
|
||||
|
||||
// d += length
|
||||
// s += length
|
||||
ADDQ CX, DI
|
||||
ADDQ CX, SI
|
||||
JMP loop
|
||||
|
||||
callMemmove:
|
||||
// if length > len(dst)-d || length > len(src)-s { etc }
|
||||
CMPQ CX, AX
|
||||
JGT errCorrupt
|
||||
CMPQ CX, BX
|
||||
JGT errCorrupt
|
||||
|
||||
// copy(dst[d:], src[s:s+length])
|
||||
//
|
||||
// This means calling runtime·memmove(&dst[d], &src[s], length), so we push
|
||||
// DI, SI and CX as arguments. Coincidentally, we also need to spill those
|
||||
// three registers to the stack, to save local variables across the CALL.
|
||||
MOVQ DI, 0(SP)
|
||||
MOVQ SI, 8(SP)
|
||||
MOVQ CX, 16(SP)
|
||||
MOVQ DI, 24(SP)
|
||||
MOVQ SI, 32(SP)
|
||||
MOVQ CX, 40(SP)
|
||||
CALL runtime·memmove(SB)
|
||||
|
||||
// Restore local variables: unspill registers from the stack and
|
||||
// re-calculate R8-R13.
|
||||
MOVQ 24(SP), DI
|
||||
MOVQ 32(SP), SI
|
||||
MOVQ 40(SP), CX
|
||||
MOVQ dst_base+0(FP), R8
|
||||
MOVQ dst_len+8(FP), R9
|
||||
MOVQ R8, R10
|
||||
ADDQ R9, R10
|
||||
MOVQ src_base+24(FP), R11
|
||||
MOVQ src_len+32(FP), R12
|
||||
MOVQ R11, R13
|
||||
ADDQ R12, R13
|
||||
|
||||
// d += length
|
||||
// s += length
|
||||
ADDQ CX, DI
|
||||
ADDQ CX, SI
|
||||
JMP loop
|
||||
|
||||
tagLit60Plus:
|
||||
// !!! This fragment does the
|
||||
//
|
||||
// s += x - 58; if uint(s) > uint(len(src)) { etc }
|
||||
//
|
||||
// checks. In the asm version, we code it once instead of once per switch case.
|
||||
ADDQ CX, SI
|
||||
SUBQ $58, SI
|
||||
MOVQ SI, BX
|
||||
SUBQ R11, BX
|
||||
CMPQ BX, R12
|
||||
JA errCorrupt
|
||||
|
||||
// case x == 60:
|
||||
CMPL CX, $61
|
||||
JEQ tagLit61
|
||||
JA tagLit62Plus
|
||||
|
||||
// x = uint32(src[s-1])
|
||||
MOVBLZX -1(SI), CX
|
||||
JMP doLit
|
||||
|
||||
tagLit61:
|
||||
// case x == 61:
|
||||
// x = uint32(src[s-2]) | uint32(src[s-1])<<8
|
||||
MOVWLZX -2(SI), CX
|
||||
JMP doLit
|
||||
|
||||
tagLit62Plus:
|
||||
CMPL CX, $62
|
||||
JA tagLit63
|
||||
|
||||
// case x == 62:
|
||||
// x = uint32(src[s-3]) | uint32(src[s-2])<<8 | uint32(src[s-1])<<16
|
||||
MOVWLZX -3(SI), CX
|
||||
MOVBLZX -1(SI), BX
|
||||
SHLL $16, BX
|
||||
ORL BX, CX
|
||||
JMP doLit
|
||||
|
||||
tagLit63:
|
||||
// case x == 63:
|
||||
// x = uint32(src[s-4]) | uint32(src[s-3])<<8 | uint32(src[s-2])<<16 | uint32(src[s-1])<<24
|
||||
MOVL -4(SI), CX
|
||||
JMP doLit
|
||||
|
||||
// The code above handles literal tags.
|
||||
// ----------------------------------------
|
||||
// The code below handles copy tags.
|
||||
|
||||
tagCopy4:
|
||||
// case tagCopy4:
|
||||
// s += 5
|
||||
ADDQ $5, SI
|
||||
|
||||
// if uint(s) > uint(len(src)) { etc }
|
||||
MOVQ SI, BX
|
||||
SUBQ R11, BX
|
||||
CMPQ BX, R12
|
||||
JA errCorrupt
|
||||
|
||||
// length = 1 + int(src[s-5])>>2
|
||||
SHRQ $2, CX
|
||||
INCQ CX
|
||||
|
||||
// offset = int(uint32(src[s-4]) | uint32(src[s-3])<<8 | uint32(src[s-2])<<16 | uint32(src[s-1])<<24)
|
||||
MOVLQZX -4(SI), DX
|
||||
JMP doCopy
|
||||
|
||||
tagCopy2:
|
||||
// case tagCopy2:
|
||||
// s += 3
|
||||
ADDQ $3, SI
|
||||
|
||||
// if uint(s) > uint(len(src)) { etc }
|
||||
MOVQ SI, BX
|
||||
SUBQ R11, BX
|
||||
CMPQ BX, R12
|
||||
JA errCorrupt
|
||||
|
||||
// length = 1 + int(src[s-3])>>2
|
||||
SHRQ $2, CX
|
||||
INCQ CX
|
||||
|
||||
// offset = int(uint32(src[s-2]) | uint32(src[s-1])<<8)
|
||||
MOVWQZX -2(SI), DX
|
||||
JMP doCopy
|
||||
|
||||
tagCopy:
|
||||
// We have a copy tag. We assume that:
|
||||
// - BX == src[s] & 0x03
|
||||
// - CX == src[s]
|
||||
CMPQ BX, $2
|
||||
JEQ tagCopy2
|
||||
JA tagCopy4
|
||||
|
||||
// case tagCopy1:
|
||||
// s += 2
|
||||
ADDQ $2, SI
|
||||
|
||||
// if uint(s) > uint(len(src)) { etc }
|
||||
MOVQ SI, BX
|
||||
SUBQ R11, BX
|
||||
CMPQ BX, R12
|
||||
JA errCorrupt
|
||||
|
||||
// offset = int(uint32(src[s-2])&0xe0<<3 | uint32(src[s-1]))
|
||||
MOVQ CX, DX
|
||||
ANDQ $0xe0, DX
|
||||
SHLQ $3, DX
|
||||
MOVBQZX -1(SI), BX
|
||||
ORQ BX, DX
|
||||
|
||||
// length = 4 + int(src[s-2])>>2&0x7
|
||||
SHRQ $2, CX
|
||||
ANDQ $7, CX
|
||||
ADDQ $4, CX
|
||||
|
||||
doCopy:
|
||||
// This is the end of the outer "switch", when we have a copy tag.
|
||||
//
|
||||
// We assume that:
|
||||
// - CX == length && CX > 0
|
||||
// - DX == offset
|
||||
|
||||
// if offset <= 0 { etc }
|
||||
CMPQ DX, $0
|
||||
JLE errCorrupt
|
||||
|
||||
// if d < offset { etc }
|
||||
MOVQ DI, BX
|
||||
SUBQ R8, BX
|
||||
CMPQ BX, DX
|
||||
JLT errCorrupt
|
||||
|
||||
// if length > len(dst)-d { etc }
|
||||
MOVQ R10, BX
|
||||
SUBQ DI, BX
|
||||
CMPQ CX, BX
|
||||
JGT errCorrupt
|
||||
|
||||
// forwardCopy(dst[d:d+length], dst[d-offset:]); d += length
|
||||
//
|
||||
// Set:
|
||||
// - R14 = len(dst)-d
|
||||
// - R15 = &dst[d-offset]
|
||||
MOVQ R10, R14
|
||||
SUBQ DI, R14
|
||||
MOVQ DI, R15
|
||||
SUBQ DX, R15
|
||||
|
||||
// !!! Try a faster technique for short (16 or fewer bytes) forward copies.
|
||||
//
|
||||
// First, try using two 8-byte load/stores, similar to the doLit technique
|
||||
// above. Even if dst[d:d+length] and dst[d-offset:] can overlap, this is
|
||||
// still OK if offset >= 8. Note that this has to be two 8-byte load/stores
|
||||
// and not one 16-byte load/store, and the first store has to be before the
|
||||
// second load, due to the overlap if offset is in the range [8, 16).
|
||||
//
|
||||
// if length > 16 || offset < 8 || len(dst)-d < 16 {
|
||||
// goto slowForwardCopy
|
||||
// }
|
||||
// copy 16 bytes
|
||||
// d += length
|
||||
CMPQ CX, $16
|
||||
JGT slowForwardCopy
|
||||
CMPQ DX, $8
|
||||
JLT slowForwardCopy
|
||||
CMPQ R14, $16
|
||||
JLT slowForwardCopy
|
||||
MOVQ 0(R15), AX
|
||||
MOVQ AX, 0(DI)
|
||||
MOVQ 8(R15), BX
|
||||
MOVQ BX, 8(DI)
|
||||
ADDQ CX, DI
|
||||
JMP loop
|
||||
|
||||
slowForwardCopy:
|
||||
// !!! If the forward copy is longer than 16 bytes, or if offset < 8, we
|
||||
// can still try 8-byte load stores, provided we can overrun up to 10 extra
|
||||
// bytes. As above, the overrun will be fixed up by subsequent iterations
|
||||
// of the outermost loop.
|
||||
//
|
||||
// The C++ snappy code calls this technique IncrementalCopyFastPath. Its
|
||||
// commentary says:
|
||||
//
|
||||
// ----
|
||||
//
|
||||
// The main part of this loop is a simple copy of eight bytes at a time
|
||||
// until we've copied (at least) the requested amount of bytes. However,
|
||||
// if d and d-offset are less than eight bytes apart (indicating a
|
||||
// repeating pattern of length < 8), we first need to expand the pattern in
|
||||
// order to get the correct results. For instance, if the buffer looks like
|
||||
// this, with the eight-byte <d-offset> and <d> patterns marked as
|
||||
// intervals:
|
||||
//
|
||||
// abxxxxxxxxxxxx
|
||||
// [------] d-offset
|
||||
// [------] d
|
||||
//
|
||||
// a single eight-byte copy from <d-offset> to <d> will repeat the pattern
|
||||
// once, after which we can move <d> two bytes without moving <d-offset>:
|
||||
//
|
||||
// ababxxxxxxxxxx
|
||||
// [------] d-offset
|
||||
// [------] d
|
||||
//
|
||||
// and repeat the exercise until the two no longer overlap.
|
||||
//
|
||||
// This allows us to do very well in the special case of one single byte
|
||||
// repeated many times, without taking a big hit for more general cases.
|
||||
//
|
||||
// The worst case of extra writing past the end of the match occurs when
|
||||
// offset == 1 and length == 1; the last copy will read from byte positions
|
||||
// [0..7] and write to [4..11], whereas it was only supposed to write to
|
||||
// position 1. Thus, ten excess bytes.
|
||||
//
|
||||
// ----
|
||||
//
|
||||
// That "10 byte overrun" worst case is confirmed by Go's
|
||||
// TestSlowForwardCopyOverrun, which also tests the fixUpSlowForwardCopy
|
||||
// and finishSlowForwardCopy algorithm.
|
||||
//
|
||||
// if length > len(dst)-d-10 {
|
||||
// goto verySlowForwardCopy
|
||||
// }
|
||||
SUBQ $10, R14
|
||||
CMPQ CX, R14
|
||||
JGT verySlowForwardCopy
|
||||
|
||||
makeOffsetAtLeast8:
|
||||
// !!! As above, expand the pattern so that offset >= 8 and we can use
|
||||
// 8-byte load/stores.
|
||||
//
|
||||
// for offset < 8 {
|
||||
// copy 8 bytes from dst[d-offset:] to dst[d:]
|
||||
// length -= offset
|
||||
// d += offset
|
||||
// offset += offset
|
||||
// // The two previous lines together means that d-offset, and therefore
|
||||
// // R15, is unchanged.
|
||||
// }
|
||||
CMPQ DX, $8
|
||||
JGE fixUpSlowForwardCopy
|
||||
MOVQ (R15), BX
|
||||
MOVQ BX, (DI)
|
||||
SUBQ DX, CX
|
||||
ADDQ DX, DI
|
||||
ADDQ DX, DX
|
||||
JMP makeOffsetAtLeast8
|
||||
|
||||
fixUpSlowForwardCopy:
|
||||
// !!! Add length (which might be negative now) to d (implied by DI being
|
||||
// &dst[d]) so that d ends up at the right place when we jump back to the
|
||||
// top of the loop. Before we do that, though, we save DI to AX so that, if
|
||||
// length is positive, copying the remaining length bytes will write to the
|
||||
// right place.
|
||||
MOVQ DI, AX
|
||||
ADDQ CX, DI
|
||||
|
||||
finishSlowForwardCopy:
|
||||
// !!! Repeat 8-byte load/stores until length <= 0. Ending with a negative
|
||||
// length means that we overrun, but as above, that will be fixed up by
|
||||
// subsequent iterations of the outermost loop.
|
||||
CMPQ CX, $0
|
||||
JLE loop
|
||||
MOVQ (R15), BX
|
||||
MOVQ BX, (AX)
|
||||
ADDQ $8, R15
|
||||
ADDQ $8, AX
|
||||
SUBQ $8, CX
|
||||
JMP finishSlowForwardCopy
|
||||
|
||||
verySlowForwardCopy:
|
||||
// verySlowForwardCopy is a simple implementation of forward copy. In C
|
||||
// parlance, this is a do/while loop instead of a while loop, since we know
|
||||
// that length > 0. In Go syntax:
|
||||
//
|
||||
// for {
|
||||
// dst[d] = dst[d - offset]
|
||||
// d++
|
||||
// length--
|
||||
// if length == 0 {
|
||||
// break
|
||||
// }
|
||||
// }
|
||||
MOVB (R15), BX
|
||||
MOVB BX, (DI)
|
||||
INCQ R15
|
||||
INCQ DI
|
||||
DECQ CX
|
||||
JNZ verySlowForwardCopy
|
||||
JMP loop
|
||||
|
||||
// The code above handles copy tags.
|
||||
// ----------------------------------------
|
||||
|
||||
end:
|
||||
// This is the end of the "for s < len(src)".
|
||||
//
|
||||
// if d != len(dst) { etc }
|
||||
CMPQ DI, R10
|
||||
JNE errCorrupt
|
||||
|
||||
// return 0
|
||||
MOVQ $0, ret+48(FP)
|
||||
RET
|
||||
|
||||
errCorrupt:
|
||||
// return decodeErrCodeCorrupt
|
||||
MOVQ $1, ret+48(FP)
|
||||
RET
|
||||
+101
@@ -0,0 +1,101 @@
|
||||
// Copyright 2016 The Snappy-Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build !amd64 appengine !gc noasm
|
||||
|
||||
package snappy
|
||||
|
||||
// decode writes the decoding of src to dst. It assumes that the varint-encoded
|
||||
// length of the decompressed bytes has already been read, and that len(dst)
|
||||
// equals that length.
|
||||
//
|
||||
// It returns 0 on success or a decodeErrCodeXxx error code on failure.
|
||||
func decode(dst, src []byte) int {
|
||||
var d, s, offset, length int
|
||||
for s < len(src) {
|
||||
switch src[s] & 0x03 {
|
||||
case tagLiteral:
|
||||
x := uint32(src[s] >> 2)
|
||||
switch {
|
||||
case x < 60:
|
||||
s++
|
||||
case x == 60:
|
||||
s += 2
|
||||
if uint(s) > uint(len(src)) { // The uint conversions catch overflow from the previous line.
|
||||
return decodeErrCodeCorrupt
|
||||
}
|
||||
x = uint32(src[s-1])
|
||||
case x == 61:
|
||||
s += 3
|
||||
if uint(s) > uint(len(src)) { // The uint conversions catch overflow from the previous line.
|
||||
return decodeErrCodeCorrupt
|
||||
}
|
||||
x = uint32(src[s-2]) | uint32(src[s-1])<<8
|
||||
case x == 62:
|
||||
s += 4
|
||||
if uint(s) > uint(len(src)) { // The uint conversions catch overflow from the previous line.
|
||||
return decodeErrCodeCorrupt
|
||||
}
|
||||
x = uint32(src[s-3]) | uint32(src[s-2])<<8 | uint32(src[s-1])<<16
|
||||
case x == 63:
|
||||
s += 5
|
||||
if uint(s) > uint(len(src)) { // The uint conversions catch overflow from the previous line.
|
||||
return decodeErrCodeCorrupt
|
||||
}
|
||||
x = uint32(src[s-4]) | uint32(src[s-3])<<8 | uint32(src[s-2])<<16 | uint32(src[s-1])<<24
|
||||
}
|
||||
length = int(x) + 1
|
||||
if length <= 0 {
|
||||
return decodeErrCodeUnsupportedLiteralLength
|
||||
}
|
||||
if length > len(dst)-d || length > len(src)-s {
|
||||
return decodeErrCodeCorrupt
|
||||
}
|
||||
copy(dst[d:], src[s:s+length])
|
||||
d += length
|
||||
s += length
|
||||
continue
|
||||
|
||||
case tagCopy1:
|
||||
s += 2
|
||||
if uint(s) > uint(len(src)) { // The uint conversions catch overflow from the previous line.
|
||||
return decodeErrCodeCorrupt
|
||||
}
|
||||
length = 4 + int(src[s-2])>>2&0x7
|
||||
offset = int(uint32(src[s-2])&0xe0<<3 | uint32(src[s-1]))
|
||||
|
||||
case tagCopy2:
|
||||
s += 3
|
||||
if uint(s) > uint(len(src)) { // The uint conversions catch overflow from the previous line.
|
||||
return decodeErrCodeCorrupt
|
||||
}
|
||||
length = 1 + int(src[s-3])>>2
|
||||
offset = int(uint32(src[s-2]) | uint32(src[s-1])<<8)
|
||||
|
||||
case tagCopy4:
|
||||
s += 5
|
||||
if uint(s) > uint(len(src)) { // The uint conversions catch overflow from the previous line.
|
||||
return decodeErrCodeCorrupt
|
||||
}
|
||||
length = 1 + int(src[s-5])>>2
|
||||
offset = int(uint32(src[s-4]) | uint32(src[s-3])<<8 | uint32(src[s-2])<<16 | uint32(src[s-1])<<24)
|
||||
}
|
||||
|
||||
if offset <= 0 || d < offset || length > len(dst)-d {
|
||||
return decodeErrCodeCorrupt
|
||||
}
|
||||
// Copy from an earlier sub-slice of dst to a later sub-slice. Unlike
|
||||
// the built-in copy function, this byte-by-byte copy always runs
|
||||
// forwards, even if the slices overlap. Conceptually, this is:
|
||||
//
|
||||
// d += forwardCopy(dst[d:d+length], dst[d-offset:])
|
||||
for end := d + length; d != end; d++ {
|
||||
dst[d] = dst[d-offset]
|
||||
}
|
||||
}
|
||||
if d != len(dst) {
|
||||
return decodeErrCodeCorrupt
|
||||
}
|
||||
return 0
|
||||
}
|
||||
+285
@@ -0,0 +1,285 @@
|
||||
// Copyright 2011 The Snappy-Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package snappy
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"io"
|
||||
)
|
||||
|
||||
// Encode returns the encoded form of src. The returned slice may be a sub-
|
||||
// slice of dst if dst was large enough to hold the entire encoded block.
|
||||
// Otherwise, a newly allocated slice will be returned.
|
||||
//
|
||||
// The dst and src must not overlap. It is valid to pass a nil dst.
|
||||
func Encode(dst, src []byte) []byte {
|
||||
if n := MaxEncodedLen(len(src)); n < 0 {
|
||||
panic(ErrTooLarge)
|
||||
} else if len(dst) < n {
|
||||
dst = make([]byte, n)
|
||||
}
|
||||
|
||||
// The block starts with the varint-encoded length of the decompressed bytes.
|
||||
d := binary.PutUvarint(dst, uint64(len(src)))
|
||||
|
||||
for len(src) > 0 {
|
||||
p := src
|
||||
src = nil
|
||||
if len(p) > maxBlockSize {
|
||||
p, src = p[:maxBlockSize], p[maxBlockSize:]
|
||||
}
|
||||
if len(p) < minNonLiteralBlockSize {
|
||||
d += emitLiteral(dst[d:], p)
|
||||
} else {
|
||||
d += encodeBlock(dst[d:], p)
|
||||
}
|
||||
}
|
||||
return dst[:d]
|
||||
}
|
||||
|
||||
// inputMargin is the minimum number of extra input bytes to keep, inside
|
||||
// encodeBlock's inner loop. On some architectures, this margin lets us
|
||||
// implement a fast path for emitLiteral, where the copy of short (<= 16 byte)
|
||||
// literals can be implemented as a single load to and store from a 16-byte
|
||||
// register. That literal's actual length can be as short as 1 byte, so this
|
||||
// can copy up to 15 bytes too much, but that's OK as subsequent iterations of
|
||||
// the encoding loop will fix up the copy overrun, and this inputMargin ensures
|
||||
// that we don't overrun the dst and src buffers.
|
||||
const inputMargin = 16 - 1
|
||||
|
||||
// minNonLiteralBlockSize is the minimum size of the input to encodeBlock that
|
||||
// could be encoded with a copy tag. This is the minimum with respect to the
|
||||
// algorithm used by encodeBlock, not a minimum enforced by the file format.
|
||||
//
|
||||
// The encoded output must start with at least a 1 byte literal, as there are
|
||||
// no previous bytes to copy. A minimal (1 byte) copy after that, generated
|
||||
// from an emitCopy call in encodeBlock's main loop, would require at least
|
||||
// another inputMargin bytes, for the reason above: we want any emitLiteral
|
||||
// calls inside encodeBlock's main loop to use the fast path if possible, which
|
||||
// requires being able to overrun by inputMargin bytes. Thus,
|
||||
// minNonLiteralBlockSize equals 1 + 1 + inputMargin.
|
||||
//
|
||||
// The C++ code doesn't use this exact threshold, but it could, as discussed at
|
||||
// https://groups.google.com/d/topic/snappy-compression/oGbhsdIJSJ8/discussion
|
||||
// The difference between Go (2+inputMargin) and C++ (inputMargin) is purely an
|
||||
// optimization. It should not affect the encoded form. This is tested by
|
||||
// TestSameEncodingAsCppShortCopies.
|
||||
const minNonLiteralBlockSize = 1 + 1 + inputMargin
|
||||
|
||||
// MaxEncodedLen returns the maximum length of a snappy block, given its
|
||||
// uncompressed length.
|
||||
//
|
||||
// It will return a negative value if srcLen is too large to encode.
|
||||
func MaxEncodedLen(srcLen int) int {
|
||||
n := uint64(srcLen)
|
||||
if n > 0xffffffff {
|
||||
return -1
|
||||
}
|
||||
// Compressed data can be defined as:
|
||||
// compressed := item* literal*
|
||||
// item := literal* copy
|
||||
//
|
||||
// The trailing literal sequence has a space blowup of at most 62/60
|
||||
// since a literal of length 60 needs one tag byte + one extra byte
|
||||
// for length information.
|
||||
//
|
||||
// Item blowup is trickier to measure. Suppose the "copy" op copies
|
||||
// 4 bytes of data. Because of a special check in the encoding code,
|
||||
// we produce a 4-byte copy only if the offset is < 65536. Therefore
|
||||
// the copy op takes 3 bytes to encode, and this type of item leads
|
||||
// to at most the 62/60 blowup for representing literals.
|
||||
//
|
||||
// Suppose the "copy" op copies 5 bytes of data. If the offset is big
|
||||
// enough, it will take 5 bytes to encode the copy op. Therefore the
|
||||
// worst case here is a one-byte literal followed by a five-byte copy.
|
||||
// That is, 6 bytes of input turn into 7 bytes of "compressed" data.
|
||||
//
|
||||
// This last factor dominates the blowup, so the final estimate is:
|
||||
n = 32 + n + n/6
|
||||
if n > 0xffffffff {
|
||||
return -1
|
||||
}
|
||||
return int(n)
|
||||
}
|
||||
|
||||
var errClosed = errors.New("snappy: Writer is closed")
|
||||
|
||||
// NewWriter returns a new Writer that compresses to w.
|
||||
//
|
||||
// The Writer returned does not buffer writes. There is no need to Flush or
|
||||
// Close such a Writer.
|
||||
//
|
||||
// Deprecated: the Writer returned is not suitable for many small writes, only
|
||||
// for few large writes. Use NewBufferedWriter instead, which is efficient
|
||||
// regardless of the frequency and shape of the writes, and remember to Close
|
||||
// that Writer when done.
|
||||
func NewWriter(w io.Writer) *Writer {
|
||||
return &Writer{
|
||||
w: w,
|
||||
obuf: make([]byte, obufLen),
|
||||
}
|
||||
}
|
||||
|
||||
// NewBufferedWriter returns a new Writer that compresses to w, using the
|
||||
// framing format described at
|
||||
// https://github.com/google/snappy/blob/master/framing_format.txt
|
||||
//
|
||||
// The Writer returned buffers writes. Users must call Close to guarantee all
|
||||
// data has been forwarded to the underlying io.Writer. They may also call
|
||||
// Flush zero or more times before calling Close.
|
||||
func NewBufferedWriter(w io.Writer) *Writer {
|
||||
return &Writer{
|
||||
w: w,
|
||||
ibuf: make([]byte, 0, maxBlockSize),
|
||||
obuf: make([]byte, obufLen),
|
||||
}
|
||||
}
|
||||
|
||||
// Writer is an io.Writer that can write Snappy-compressed bytes.
|
||||
type Writer struct {
|
||||
w io.Writer
|
||||
err error
|
||||
|
||||
// ibuf is a buffer for the incoming (uncompressed) bytes.
|
||||
//
|
||||
// Its use is optional. For backwards compatibility, Writers created by the
|
||||
// NewWriter function have ibuf == nil, do not buffer incoming bytes, and
|
||||
// therefore do not need to be Flush'ed or Close'd.
|
||||
ibuf []byte
|
||||
|
||||
// obuf is a buffer for the outgoing (compressed) bytes.
|
||||
obuf []byte
|
||||
|
||||
// wroteStreamHeader is whether we have written the stream header.
|
||||
wroteStreamHeader bool
|
||||
}
|
||||
|
||||
// Reset discards the writer's state and switches the Snappy writer to write to
|
||||
// w. This permits reusing a Writer rather than allocating a new one.
|
||||
func (w *Writer) Reset(writer io.Writer) {
|
||||
w.w = writer
|
||||
w.err = nil
|
||||
if w.ibuf != nil {
|
||||
w.ibuf = w.ibuf[:0]
|
||||
}
|
||||
w.wroteStreamHeader = false
|
||||
}
|
||||
|
||||
// Write satisfies the io.Writer interface.
|
||||
func (w *Writer) Write(p []byte) (nRet int, errRet error) {
|
||||
if w.ibuf == nil {
|
||||
// Do not buffer incoming bytes. This does not perform or compress well
|
||||
// if the caller of Writer.Write writes many small slices. This
|
||||
// behavior is therefore deprecated, but still supported for backwards
|
||||
// compatibility with code that doesn't explicitly Flush or Close.
|
||||
return w.write(p)
|
||||
}
|
||||
|
||||
// The remainder of this method is based on bufio.Writer.Write from the
|
||||
// standard library.
|
||||
|
||||
for len(p) > (cap(w.ibuf)-len(w.ibuf)) && w.err == nil {
|
||||
var n int
|
||||
if len(w.ibuf) == 0 {
|
||||
// Large write, empty buffer.
|
||||
// Write directly from p to avoid copy.
|
||||
n, _ = w.write(p)
|
||||
} else {
|
||||
n = copy(w.ibuf[len(w.ibuf):cap(w.ibuf)], p)
|
||||
w.ibuf = w.ibuf[:len(w.ibuf)+n]
|
||||
w.Flush()
|
||||
}
|
||||
nRet += n
|
||||
p = p[n:]
|
||||
}
|
||||
if w.err != nil {
|
||||
return nRet, w.err
|
||||
}
|
||||
n := copy(w.ibuf[len(w.ibuf):cap(w.ibuf)], p)
|
||||
w.ibuf = w.ibuf[:len(w.ibuf)+n]
|
||||
nRet += n
|
||||
return nRet, nil
|
||||
}
|
||||
|
||||
func (w *Writer) write(p []byte) (nRet int, errRet error) {
|
||||
if w.err != nil {
|
||||
return 0, w.err
|
||||
}
|
||||
for len(p) > 0 {
|
||||
obufStart := len(magicChunk)
|
||||
if !w.wroteStreamHeader {
|
||||
w.wroteStreamHeader = true
|
||||
copy(w.obuf, magicChunk)
|
||||
obufStart = 0
|
||||
}
|
||||
|
||||
var uncompressed []byte
|
||||
if len(p) > maxBlockSize {
|
||||
uncompressed, p = p[:maxBlockSize], p[maxBlockSize:]
|
||||
} else {
|
||||
uncompressed, p = p, nil
|
||||
}
|
||||
checksum := crc(uncompressed)
|
||||
|
||||
// Compress the buffer, discarding the result if the improvement
|
||||
// isn't at least 12.5%.
|
||||
compressed := Encode(w.obuf[obufHeaderLen:], uncompressed)
|
||||
chunkType := uint8(chunkTypeCompressedData)
|
||||
chunkLen := 4 + len(compressed)
|
||||
obufEnd := obufHeaderLen + len(compressed)
|
||||
if len(compressed) >= len(uncompressed)-len(uncompressed)/8 {
|
||||
chunkType = chunkTypeUncompressedData
|
||||
chunkLen = 4 + len(uncompressed)
|
||||
obufEnd = obufHeaderLen
|
||||
}
|
||||
|
||||
// Fill in the per-chunk header that comes before the body.
|
||||
w.obuf[len(magicChunk)+0] = chunkType
|
||||
w.obuf[len(magicChunk)+1] = uint8(chunkLen >> 0)
|
||||
w.obuf[len(magicChunk)+2] = uint8(chunkLen >> 8)
|
||||
w.obuf[len(magicChunk)+3] = uint8(chunkLen >> 16)
|
||||
w.obuf[len(magicChunk)+4] = uint8(checksum >> 0)
|
||||
w.obuf[len(magicChunk)+5] = uint8(checksum >> 8)
|
||||
w.obuf[len(magicChunk)+6] = uint8(checksum >> 16)
|
||||
w.obuf[len(magicChunk)+7] = uint8(checksum >> 24)
|
||||
|
||||
if _, err := w.w.Write(w.obuf[obufStart:obufEnd]); err != nil {
|
||||
w.err = err
|
||||
return nRet, err
|
||||
}
|
||||
if chunkType == chunkTypeUncompressedData {
|
||||
if _, err := w.w.Write(uncompressed); err != nil {
|
||||
w.err = err
|
||||
return nRet, err
|
||||
}
|
||||
}
|
||||
nRet += len(uncompressed)
|
||||
}
|
||||
return nRet, nil
|
||||
}
|
||||
|
||||
// Flush flushes the Writer to its underlying io.Writer.
|
||||
func (w *Writer) Flush() error {
|
||||
if w.err != nil {
|
||||
return w.err
|
||||
}
|
||||
if len(w.ibuf) == 0 {
|
||||
return nil
|
||||
}
|
||||
w.write(w.ibuf)
|
||||
w.ibuf = w.ibuf[:0]
|
||||
return w.err
|
||||
}
|
||||
|
||||
// Close calls Flush and then closes the Writer.
|
||||
func (w *Writer) Close() error {
|
||||
w.Flush()
|
||||
ret := w.err
|
||||
if w.err == nil {
|
||||
w.err = errClosed
|
||||
}
|
||||
return ret
|
||||
}
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
// Copyright 2016 The Snappy-Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build !appengine
|
||||
// +build gc
|
||||
// +build !noasm
|
||||
|
||||
package snappy
|
||||
|
||||
// emitLiteral has the same semantics as in encode_other.go.
|
||||
//
|
||||
//go:noescape
|
||||
func emitLiteral(dst, lit []byte) int
|
||||
|
||||
// emitCopy has the same semantics as in encode_other.go.
|
||||
//
|
||||
//go:noescape
|
||||
func emitCopy(dst []byte, offset, length int) int
|
||||
|
||||
// extendMatch has the same semantics as in encode_other.go.
|
||||
//
|
||||
//go:noescape
|
||||
func extendMatch(src []byte, i, j int) int
|
||||
|
||||
// encodeBlock has the same semantics as in encode_other.go.
|
||||
//
|
||||
//go:noescape
|
||||
func encodeBlock(dst, src []byte) (d int)
|
||||
+730
@@ -0,0 +1,730 @@
|
||||
// Copyright 2016 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build !appengine
|
||||
// +build gc
|
||||
// +build !noasm
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
// The XXX lines assemble on Go 1.4, 1.5 and 1.7, but not 1.6, due to a
|
||||
// Go toolchain regression. See https://github.com/golang/go/issues/15426 and
|
||||
// https://github.com/golang/snappy/issues/29
|
||||
//
|
||||
// As a workaround, the package was built with a known good assembler, and
|
||||
// those instructions were disassembled by "objdump -d" to yield the
|
||||
// 4e 0f b7 7c 5c 78 movzwq 0x78(%rsp,%r11,2),%r15
|
||||
// style comments, in AT&T asm syntax. Note that rsp here is a physical
|
||||
// register, not Go/asm's SP pseudo-register (see https://golang.org/doc/asm).
|
||||
// The instructions were then encoded as "BYTE $0x.." sequences, which assemble
|
||||
// fine on Go 1.6.
|
||||
|
||||
// The asm code generally follows the pure Go code in encode_other.go, except
|
||||
// where marked with a "!!!".
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
// func emitLiteral(dst, lit []byte) int
|
||||
//
|
||||
// All local variables fit into registers. The register allocation:
|
||||
// - AX len(lit)
|
||||
// - BX n
|
||||
// - DX return value
|
||||
// - DI &dst[i]
|
||||
// - R10 &lit[0]
|
||||
//
|
||||
// The 24 bytes of stack space is to call runtime·memmove.
|
||||
//
|
||||
// The unusual register allocation of local variables, such as R10 for the
|
||||
// source pointer, matches the allocation used at the call site in encodeBlock,
|
||||
// which makes it easier to manually inline this function.
|
||||
TEXT ·emitLiteral(SB), NOSPLIT, $24-56
|
||||
MOVQ dst_base+0(FP), DI
|
||||
MOVQ lit_base+24(FP), R10
|
||||
MOVQ lit_len+32(FP), AX
|
||||
MOVQ AX, DX
|
||||
MOVL AX, BX
|
||||
SUBL $1, BX
|
||||
|
||||
CMPL BX, $60
|
||||
JLT oneByte
|
||||
CMPL BX, $256
|
||||
JLT twoBytes
|
||||
|
||||
threeBytes:
|
||||
MOVB $0xf4, 0(DI)
|
||||
MOVW BX, 1(DI)
|
||||
ADDQ $3, DI
|
||||
ADDQ $3, DX
|
||||
JMP memmove
|
||||
|
||||
twoBytes:
|
||||
MOVB $0xf0, 0(DI)
|
||||
MOVB BX, 1(DI)
|
||||
ADDQ $2, DI
|
||||
ADDQ $2, DX
|
||||
JMP memmove
|
||||
|
||||
oneByte:
|
||||
SHLB $2, BX
|
||||
MOVB BX, 0(DI)
|
||||
ADDQ $1, DI
|
||||
ADDQ $1, DX
|
||||
|
||||
memmove:
|
||||
MOVQ DX, ret+48(FP)
|
||||
|
||||
// copy(dst[i:], lit)
|
||||
//
|
||||
// This means calling runtime·memmove(&dst[i], &lit[0], len(lit)), so we push
|
||||
// DI, R10 and AX as arguments.
|
||||
MOVQ DI, 0(SP)
|
||||
MOVQ R10, 8(SP)
|
||||
MOVQ AX, 16(SP)
|
||||
CALL runtime·memmove(SB)
|
||||
RET
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
// func emitCopy(dst []byte, offset, length int) int
|
||||
//
|
||||
// All local variables fit into registers. The register allocation:
|
||||
// - AX length
|
||||
// - SI &dst[0]
|
||||
// - DI &dst[i]
|
||||
// - R11 offset
|
||||
//
|
||||
// The unusual register allocation of local variables, such as R11 for the
|
||||
// offset, matches the allocation used at the call site in encodeBlock, which
|
||||
// makes it easier to manually inline this function.
|
||||
TEXT ·emitCopy(SB), NOSPLIT, $0-48
|
||||
MOVQ dst_base+0(FP), DI
|
||||
MOVQ DI, SI
|
||||
MOVQ offset+24(FP), R11
|
||||
MOVQ length+32(FP), AX
|
||||
|
||||
loop0:
|
||||
// for length >= 68 { etc }
|
||||
CMPL AX, $68
|
||||
JLT step1
|
||||
|
||||
// Emit a length 64 copy, encoded as 3 bytes.
|
||||
MOVB $0xfe, 0(DI)
|
||||
MOVW R11, 1(DI)
|
||||
ADDQ $3, DI
|
||||
SUBL $64, AX
|
||||
JMP loop0
|
||||
|
||||
step1:
|
||||
// if length > 64 { etc }
|
||||
CMPL AX, $64
|
||||
JLE step2
|
||||
|
||||
// Emit a length 60 copy, encoded as 3 bytes.
|
||||
MOVB $0xee, 0(DI)
|
||||
MOVW R11, 1(DI)
|
||||
ADDQ $3, DI
|
||||
SUBL $60, AX
|
||||
|
||||
step2:
|
||||
// if length >= 12 || offset >= 2048 { goto step3 }
|
||||
CMPL AX, $12
|
||||
JGE step3
|
||||
CMPL R11, $2048
|
||||
JGE step3
|
||||
|
||||
// Emit the remaining copy, encoded as 2 bytes.
|
||||
MOVB R11, 1(DI)
|
||||
SHRL $8, R11
|
||||
SHLB $5, R11
|
||||
SUBB $4, AX
|
||||
SHLB $2, AX
|
||||
ORB AX, R11
|
||||
ORB $1, R11
|
||||
MOVB R11, 0(DI)
|
||||
ADDQ $2, DI
|
||||
|
||||
// Return the number of bytes written.
|
||||
SUBQ SI, DI
|
||||
MOVQ DI, ret+40(FP)
|
||||
RET
|
||||
|
||||
step3:
|
||||
// Emit the remaining copy, encoded as 3 bytes.
|
||||
SUBL $1, AX
|
||||
SHLB $2, AX
|
||||
ORB $2, AX
|
||||
MOVB AX, 0(DI)
|
||||
MOVW R11, 1(DI)
|
||||
ADDQ $3, DI
|
||||
|
||||
// Return the number of bytes written.
|
||||
SUBQ SI, DI
|
||||
MOVQ DI, ret+40(FP)
|
||||
RET
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
// func extendMatch(src []byte, i, j int) int
|
||||
//
|
||||
// All local variables fit into registers. The register allocation:
|
||||
// - DX &src[0]
|
||||
// - SI &src[j]
|
||||
// - R13 &src[len(src) - 8]
|
||||
// - R14 &src[len(src)]
|
||||
// - R15 &src[i]
|
||||
//
|
||||
// The unusual register allocation of local variables, such as R15 for a source
|
||||
// pointer, matches the allocation used at the call site in encodeBlock, which
|
||||
// makes it easier to manually inline this function.
|
||||
TEXT ·extendMatch(SB), NOSPLIT, $0-48
|
||||
MOVQ src_base+0(FP), DX
|
||||
MOVQ src_len+8(FP), R14
|
||||
MOVQ i+24(FP), R15
|
||||
MOVQ j+32(FP), SI
|
||||
ADDQ DX, R14
|
||||
ADDQ DX, R15
|
||||
ADDQ DX, SI
|
||||
MOVQ R14, R13
|
||||
SUBQ $8, R13
|
||||
|
||||
cmp8:
|
||||
// As long as we are 8 or more bytes before the end of src, we can load and
|
||||
// compare 8 bytes at a time. If those 8 bytes are equal, repeat.
|
||||
CMPQ SI, R13
|
||||
JA cmp1
|
||||
MOVQ (R15), AX
|
||||
MOVQ (SI), BX
|
||||
CMPQ AX, BX
|
||||
JNE bsf
|
||||
ADDQ $8, R15
|
||||
ADDQ $8, SI
|
||||
JMP cmp8
|
||||
|
||||
bsf:
|
||||
// If those 8 bytes were not equal, XOR the two 8 byte values, and return
|
||||
// the index of the first byte that differs. The BSF instruction finds the
|
||||
// least significant 1 bit, the amd64 architecture is little-endian, and
|
||||
// the shift by 3 converts a bit index to a byte index.
|
||||
XORQ AX, BX
|
||||
BSFQ BX, BX
|
||||
SHRQ $3, BX
|
||||
ADDQ BX, SI
|
||||
|
||||
// Convert from &src[ret] to ret.
|
||||
SUBQ DX, SI
|
||||
MOVQ SI, ret+40(FP)
|
||||
RET
|
||||
|
||||
cmp1:
|
||||
// In src's tail, compare 1 byte at a time.
|
||||
CMPQ SI, R14
|
||||
JAE extendMatchEnd
|
||||
MOVB (R15), AX
|
||||
MOVB (SI), BX
|
||||
CMPB AX, BX
|
||||
JNE extendMatchEnd
|
||||
ADDQ $1, R15
|
||||
ADDQ $1, SI
|
||||
JMP cmp1
|
||||
|
||||
extendMatchEnd:
|
||||
// Convert from &src[ret] to ret.
|
||||
SUBQ DX, SI
|
||||
MOVQ SI, ret+40(FP)
|
||||
RET
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
// func encodeBlock(dst, src []byte) (d int)
|
||||
//
|
||||
// All local variables fit into registers, other than "var table". The register
|
||||
// allocation:
|
||||
// - AX . .
|
||||
// - BX . .
|
||||
// - CX 56 shift (note that amd64 shifts by non-immediates must use CX).
|
||||
// - DX 64 &src[0], tableSize
|
||||
// - SI 72 &src[s]
|
||||
// - DI 80 &dst[d]
|
||||
// - R9 88 sLimit
|
||||
// - R10 . &src[nextEmit]
|
||||
// - R11 96 prevHash, currHash, nextHash, offset
|
||||
// - R12 104 &src[base], skip
|
||||
// - R13 . &src[nextS], &src[len(src) - 8]
|
||||
// - R14 . len(src), bytesBetweenHashLookups, &src[len(src)], x
|
||||
// - R15 112 candidate
|
||||
//
|
||||
// The second column (56, 64, etc) is the stack offset to spill the registers
|
||||
// when calling other functions. We could pack this slightly tighter, but it's
|
||||
// simpler to have a dedicated spill map independent of the function called.
|
||||
//
|
||||
// "var table [maxTableSize]uint16" takes up 32768 bytes of stack space. An
|
||||
// extra 56 bytes, to call other functions, and an extra 64 bytes, to spill
|
||||
// local variables (registers) during calls gives 32768 + 56 + 64 = 32888.
|
||||
TEXT ·encodeBlock(SB), 0, $32888-56
|
||||
MOVQ dst_base+0(FP), DI
|
||||
MOVQ src_base+24(FP), SI
|
||||
MOVQ src_len+32(FP), R14
|
||||
|
||||
// shift, tableSize := uint32(32-8), 1<<8
|
||||
MOVQ $24, CX
|
||||
MOVQ $256, DX
|
||||
|
||||
calcShift:
|
||||
// for ; tableSize < maxTableSize && tableSize < len(src); tableSize *= 2 {
|
||||
// shift--
|
||||
// }
|
||||
CMPQ DX, $16384
|
||||
JGE varTable
|
||||
CMPQ DX, R14
|
||||
JGE varTable
|
||||
SUBQ $1, CX
|
||||
SHLQ $1, DX
|
||||
JMP calcShift
|
||||
|
||||
varTable:
|
||||
// var table [maxTableSize]uint16
|
||||
//
|
||||
// In the asm code, unlike the Go code, we can zero-initialize only the
|
||||
// first tableSize elements. Each uint16 element is 2 bytes and each MOVOU
|
||||
// writes 16 bytes, so we can do only tableSize/8 writes instead of the
|
||||
// 2048 writes that would zero-initialize all of table's 32768 bytes.
|
||||
SHRQ $3, DX
|
||||
LEAQ table-32768(SP), BX
|
||||
PXOR X0, X0
|
||||
|
||||
memclr:
|
||||
MOVOU X0, 0(BX)
|
||||
ADDQ $16, BX
|
||||
SUBQ $1, DX
|
||||
JNZ memclr
|
||||
|
||||
// !!! DX = &src[0]
|
||||
MOVQ SI, DX
|
||||
|
||||
// sLimit := len(src) - inputMargin
|
||||
MOVQ R14, R9
|
||||
SUBQ $15, R9
|
||||
|
||||
// !!! Pre-emptively spill CX, DX and R9 to the stack. Their values don't
|
||||
// change for the rest of the function.
|
||||
MOVQ CX, 56(SP)
|
||||
MOVQ DX, 64(SP)
|
||||
MOVQ R9, 88(SP)
|
||||
|
||||
// nextEmit := 0
|
||||
MOVQ DX, R10
|
||||
|
||||
// s := 1
|
||||
ADDQ $1, SI
|
||||
|
||||
// nextHash := hash(load32(src, s), shift)
|
||||
MOVL 0(SI), R11
|
||||
IMULL $0x1e35a7bd, R11
|
||||
SHRL CX, R11
|
||||
|
||||
outer:
|
||||
// for { etc }
|
||||
|
||||
// skip := 32
|
||||
MOVQ $32, R12
|
||||
|
||||
// nextS := s
|
||||
MOVQ SI, R13
|
||||
|
||||
// candidate := 0
|
||||
MOVQ $0, R15
|
||||
|
||||
inner0:
|
||||
// for { etc }
|
||||
|
||||
// s := nextS
|
||||
MOVQ R13, SI
|
||||
|
||||
// bytesBetweenHashLookups := skip >> 5
|
||||
MOVQ R12, R14
|
||||
SHRQ $5, R14
|
||||
|
||||
// nextS = s + bytesBetweenHashLookups
|
||||
ADDQ R14, R13
|
||||
|
||||
// skip += bytesBetweenHashLookups
|
||||
ADDQ R14, R12
|
||||
|
||||
// if nextS > sLimit { goto emitRemainder }
|
||||
MOVQ R13, AX
|
||||
SUBQ DX, AX
|
||||
CMPQ AX, R9
|
||||
JA emitRemainder
|
||||
|
||||
// candidate = int(table[nextHash])
|
||||
// XXX: MOVWQZX table-32768(SP)(R11*2), R15
|
||||
// XXX: 4e 0f b7 7c 5c 78 movzwq 0x78(%rsp,%r11,2),%r15
|
||||
BYTE $0x4e
|
||||
BYTE $0x0f
|
||||
BYTE $0xb7
|
||||
BYTE $0x7c
|
||||
BYTE $0x5c
|
||||
BYTE $0x78
|
||||
|
||||
// table[nextHash] = uint16(s)
|
||||
MOVQ SI, AX
|
||||
SUBQ DX, AX
|
||||
|
||||
// XXX: MOVW AX, table-32768(SP)(R11*2)
|
||||
// XXX: 66 42 89 44 5c 78 mov %ax,0x78(%rsp,%r11,2)
|
||||
BYTE $0x66
|
||||
BYTE $0x42
|
||||
BYTE $0x89
|
||||
BYTE $0x44
|
||||
BYTE $0x5c
|
||||
BYTE $0x78
|
||||
|
||||
// nextHash = hash(load32(src, nextS), shift)
|
||||
MOVL 0(R13), R11
|
||||
IMULL $0x1e35a7bd, R11
|
||||
SHRL CX, R11
|
||||
|
||||
// if load32(src, s) != load32(src, candidate) { continue } break
|
||||
MOVL 0(SI), AX
|
||||
MOVL (DX)(R15*1), BX
|
||||
CMPL AX, BX
|
||||
JNE inner0
|
||||
|
||||
fourByteMatch:
|
||||
// As per the encode_other.go code:
|
||||
//
|
||||
// A 4-byte match has been found. We'll later see etc.
|
||||
|
||||
// !!! Jump to a fast path for short (<= 16 byte) literals. See the comment
|
||||
// on inputMargin in encode.go.
|
||||
MOVQ SI, AX
|
||||
SUBQ R10, AX
|
||||
CMPQ AX, $16
|
||||
JLE emitLiteralFastPath
|
||||
|
||||
// ----------------------------------------
|
||||
// Begin inline of the emitLiteral call.
|
||||
//
|
||||
// d += emitLiteral(dst[d:], src[nextEmit:s])
|
||||
|
||||
MOVL AX, BX
|
||||
SUBL $1, BX
|
||||
|
||||
CMPL BX, $60
|
||||
JLT inlineEmitLiteralOneByte
|
||||
CMPL BX, $256
|
||||
JLT inlineEmitLiteralTwoBytes
|
||||
|
||||
inlineEmitLiteralThreeBytes:
|
||||
MOVB $0xf4, 0(DI)
|
||||
MOVW BX, 1(DI)
|
||||
ADDQ $3, DI
|
||||
JMP inlineEmitLiteralMemmove
|
||||
|
||||
inlineEmitLiteralTwoBytes:
|
||||
MOVB $0xf0, 0(DI)
|
||||
MOVB BX, 1(DI)
|
||||
ADDQ $2, DI
|
||||
JMP inlineEmitLiteralMemmove
|
||||
|
||||
inlineEmitLiteralOneByte:
|
||||
SHLB $2, BX
|
||||
MOVB BX, 0(DI)
|
||||
ADDQ $1, DI
|
||||
|
||||
inlineEmitLiteralMemmove:
|
||||
// Spill local variables (registers) onto the stack; call; unspill.
|
||||
//
|
||||
// copy(dst[i:], lit)
|
||||
//
|
||||
// This means calling runtime·memmove(&dst[i], &lit[0], len(lit)), so we push
|
||||
// DI, R10 and AX as arguments.
|
||||
MOVQ DI, 0(SP)
|
||||
MOVQ R10, 8(SP)
|
||||
MOVQ AX, 16(SP)
|
||||
ADDQ AX, DI // Finish the "d +=" part of "d += emitLiteral(etc)".
|
||||
MOVQ SI, 72(SP)
|
||||
MOVQ DI, 80(SP)
|
||||
MOVQ R15, 112(SP)
|
||||
CALL runtime·memmove(SB)
|
||||
MOVQ 56(SP), CX
|
||||
MOVQ 64(SP), DX
|
||||
MOVQ 72(SP), SI
|
||||
MOVQ 80(SP), DI
|
||||
MOVQ 88(SP), R9
|
||||
MOVQ 112(SP), R15
|
||||
JMP inner1
|
||||
|
||||
inlineEmitLiteralEnd:
|
||||
// End inline of the emitLiteral call.
|
||||
// ----------------------------------------
|
||||
|
||||
emitLiteralFastPath:
|
||||
// !!! Emit the 1-byte encoding "uint8(len(lit)-1)<<2".
|
||||
MOVB AX, BX
|
||||
SUBB $1, BX
|
||||
SHLB $2, BX
|
||||
MOVB BX, (DI)
|
||||
ADDQ $1, DI
|
||||
|
||||
// !!! Implement the copy from lit to dst as a 16-byte load and store.
|
||||
// (Encode's documentation says that dst and src must not overlap.)
|
||||
//
|
||||
// This always copies 16 bytes, instead of only len(lit) bytes, but that's
|
||||
// OK. Subsequent iterations will fix up the overrun.
|
||||
//
|
||||
// Note that on amd64, it is legal and cheap to issue unaligned 8-byte or
|
||||
// 16-byte loads and stores. This technique probably wouldn't be as
|
||||
// effective on architectures that are fussier about alignment.
|
||||
MOVOU 0(R10), X0
|
||||
MOVOU X0, 0(DI)
|
||||
ADDQ AX, DI
|
||||
|
||||
inner1:
|
||||
// for { etc }
|
||||
|
||||
// base := s
|
||||
MOVQ SI, R12
|
||||
|
||||
// !!! offset := base - candidate
|
||||
MOVQ R12, R11
|
||||
SUBQ R15, R11
|
||||
SUBQ DX, R11
|
||||
|
||||
// ----------------------------------------
|
||||
// Begin inline of the extendMatch call.
|
||||
//
|
||||
// s = extendMatch(src, candidate+4, s+4)
|
||||
|
||||
// !!! R14 = &src[len(src)]
|
||||
MOVQ src_len+32(FP), R14
|
||||
ADDQ DX, R14
|
||||
|
||||
// !!! R13 = &src[len(src) - 8]
|
||||
MOVQ R14, R13
|
||||
SUBQ $8, R13
|
||||
|
||||
// !!! R15 = &src[candidate + 4]
|
||||
ADDQ $4, R15
|
||||
ADDQ DX, R15
|
||||
|
||||
// !!! s += 4
|
||||
ADDQ $4, SI
|
||||
|
||||
inlineExtendMatchCmp8:
|
||||
// As long as we are 8 or more bytes before the end of src, we can load and
|
||||
// compare 8 bytes at a time. If those 8 bytes are equal, repeat.
|
||||
CMPQ SI, R13
|
||||
JA inlineExtendMatchCmp1
|
||||
MOVQ (R15), AX
|
||||
MOVQ (SI), BX
|
||||
CMPQ AX, BX
|
||||
JNE inlineExtendMatchBSF
|
||||
ADDQ $8, R15
|
||||
ADDQ $8, SI
|
||||
JMP inlineExtendMatchCmp8
|
||||
|
||||
inlineExtendMatchBSF:
|
||||
// If those 8 bytes were not equal, XOR the two 8 byte values, and return
|
||||
// the index of the first byte that differs. The BSF instruction finds the
|
||||
// least significant 1 bit, the amd64 architecture is little-endian, and
|
||||
// the shift by 3 converts a bit index to a byte index.
|
||||
XORQ AX, BX
|
||||
BSFQ BX, BX
|
||||
SHRQ $3, BX
|
||||
ADDQ BX, SI
|
||||
JMP inlineExtendMatchEnd
|
||||
|
||||
inlineExtendMatchCmp1:
|
||||
// In src's tail, compare 1 byte at a time.
|
||||
CMPQ SI, R14
|
||||
JAE inlineExtendMatchEnd
|
||||
MOVB (R15), AX
|
||||
MOVB (SI), BX
|
||||
CMPB AX, BX
|
||||
JNE inlineExtendMatchEnd
|
||||
ADDQ $1, R15
|
||||
ADDQ $1, SI
|
||||
JMP inlineExtendMatchCmp1
|
||||
|
||||
inlineExtendMatchEnd:
|
||||
// End inline of the extendMatch call.
|
||||
// ----------------------------------------
|
||||
|
||||
// ----------------------------------------
|
||||
// Begin inline of the emitCopy call.
|
||||
//
|
||||
// d += emitCopy(dst[d:], base-candidate, s-base)
|
||||
|
||||
// !!! length := s - base
|
||||
MOVQ SI, AX
|
||||
SUBQ R12, AX
|
||||
|
||||
inlineEmitCopyLoop0:
|
||||
// for length >= 68 { etc }
|
||||
CMPL AX, $68
|
||||
JLT inlineEmitCopyStep1
|
||||
|
||||
// Emit a length 64 copy, encoded as 3 bytes.
|
||||
MOVB $0xfe, 0(DI)
|
||||
MOVW R11, 1(DI)
|
||||
ADDQ $3, DI
|
||||
SUBL $64, AX
|
||||
JMP inlineEmitCopyLoop0
|
||||
|
||||
inlineEmitCopyStep1:
|
||||
// if length > 64 { etc }
|
||||
CMPL AX, $64
|
||||
JLE inlineEmitCopyStep2
|
||||
|
||||
// Emit a length 60 copy, encoded as 3 bytes.
|
||||
MOVB $0xee, 0(DI)
|
||||
MOVW R11, 1(DI)
|
||||
ADDQ $3, DI
|
||||
SUBL $60, AX
|
||||
|
||||
inlineEmitCopyStep2:
|
||||
// if length >= 12 || offset >= 2048 { goto inlineEmitCopyStep3 }
|
||||
CMPL AX, $12
|
||||
JGE inlineEmitCopyStep3
|
||||
CMPL R11, $2048
|
||||
JGE inlineEmitCopyStep3
|
||||
|
||||
// Emit the remaining copy, encoded as 2 bytes.
|
||||
MOVB R11, 1(DI)
|
||||
SHRL $8, R11
|
||||
SHLB $5, R11
|
||||
SUBB $4, AX
|
||||
SHLB $2, AX
|
||||
ORB AX, R11
|
||||
ORB $1, R11
|
||||
MOVB R11, 0(DI)
|
||||
ADDQ $2, DI
|
||||
JMP inlineEmitCopyEnd
|
||||
|
||||
inlineEmitCopyStep3:
|
||||
// Emit the remaining copy, encoded as 3 bytes.
|
||||
SUBL $1, AX
|
||||
SHLB $2, AX
|
||||
ORB $2, AX
|
||||
MOVB AX, 0(DI)
|
||||
MOVW R11, 1(DI)
|
||||
ADDQ $3, DI
|
||||
|
||||
inlineEmitCopyEnd:
|
||||
// End inline of the emitCopy call.
|
||||
// ----------------------------------------
|
||||
|
||||
// nextEmit = s
|
||||
MOVQ SI, R10
|
||||
|
||||
// if s >= sLimit { goto emitRemainder }
|
||||
MOVQ SI, AX
|
||||
SUBQ DX, AX
|
||||
CMPQ AX, R9
|
||||
JAE emitRemainder
|
||||
|
||||
// As per the encode_other.go code:
|
||||
//
|
||||
// We could immediately etc.
|
||||
|
||||
// x := load64(src, s-1)
|
||||
MOVQ -1(SI), R14
|
||||
|
||||
// prevHash := hash(uint32(x>>0), shift)
|
||||
MOVL R14, R11
|
||||
IMULL $0x1e35a7bd, R11
|
||||
SHRL CX, R11
|
||||
|
||||
// table[prevHash] = uint16(s-1)
|
||||
MOVQ SI, AX
|
||||
SUBQ DX, AX
|
||||
SUBQ $1, AX
|
||||
|
||||
// XXX: MOVW AX, table-32768(SP)(R11*2)
|
||||
// XXX: 66 42 89 44 5c 78 mov %ax,0x78(%rsp,%r11,2)
|
||||
BYTE $0x66
|
||||
BYTE $0x42
|
||||
BYTE $0x89
|
||||
BYTE $0x44
|
||||
BYTE $0x5c
|
||||
BYTE $0x78
|
||||
|
||||
// currHash := hash(uint32(x>>8), shift)
|
||||
SHRQ $8, R14
|
||||
MOVL R14, R11
|
||||
IMULL $0x1e35a7bd, R11
|
||||
SHRL CX, R11
|
||||
|
||||
// candidate = int(table[currHash])
|
||||
// XXX: MOVWQZX table-32768(SP)(R11*2), R15
|
||||
// XXX: 4e 0f b7 7c 5c 78 movzwq 0x78(%rsp,%r11,2),%r15
|
||||
BYTE $0x4e
|
||||
BYTE $0x0f
|
||||
BYTE $0xb7
|
||||
BYTE $0x7c
|
||||
BYTE $0x5c
|
||||
BYTE $0x78
|
||||
|
||||
// table[currHash] = uint16(s)
|
||||
ADDQ $1, AX
|
||||
|
||||
// XXX: MOVW AX, table-32768(SP)(R11*2)
|
||||
// XXX: 66 42 89 44 5c 78 mov %ax,0x78(%rsp,%r11,2)
|
||||
BYTE $0x66
|
||||
BYTE $0x42
|
||||
BYTE $0x89
|
||||
BYTE $0x44
|
||||
BYTE $0x5c
|
||||
BYTE $0x78
|
||||
|
||||
// if uint32(x>>8) == load32(src, candidate) { continue }
|
||||
MOVL (DX)(R15*1), BX
|
||||
CMPL R14, BX
|
||||
JEQ inner1
|
||||
|
||||
// nextHash = hash(uint32(x>>16), shift)
|
||||
SHRQ $8, R14
|
||||
MOVL R14, R11
|
||||
IMULL $0x1e35a7bd, R11
|
||||
SHRL CX, R11
|
||||
|
||||
// s++
|
||||
ADDQ $1, SI
|
||||
|
||||
// break out of the inner1 for loop, i.e. continue the outer loop.
|
||||
JMP outer
|
||||
|
||||
emitRemainder:
|
||||
// if nextEmit < len(src) { etc }
|
||||
MOVQ src_len+32(FP), AX
|
||||
ADDQ DX, AX
|
||||
CMPQ R10, AX
|
||||
JEQ encodeBlockEnd
|
||||
|
||||
// d += emitLiteral(dst[d:], src[nextEmit:])
|
||||
//
|
||||
// Push args.
|
||||
MOVQ DI, 0(SP)
|
||||
MOVQ $0, 8(SP) // Unnecessary, as the callee ignores it, but conservative.
|
||||
MOVQ $0, 16(SP) // Unnecessary, as the callee ignores it, but conservative.
|
||||
MOVQ R10, 24(SP)
|
||||
SUBQ R10, AX
|
||||
MOVQ AX, 32(SP)
|
||||
MOVQ AX, 40(SP) // Unnecessary, as the callee ignores it, but conservative.
|
||||
|
||||
// Spill local variables (registers) onto the stack; call; unspill.
|
||||
MOVQ DI, 80(SP)
|
||||
CALL ·emitLiteral(SB)
|
||||
MOVQ 80(SP), DI
|
||||
|
||||
// Finish the "d +=" part of "d += emitLiteral(etc)".
|
||||
ADDQ 48(SP), DI
|
||||
|
||||
encodeBlockEnd:
|
||||
MOVQ dst_base+0(FP), AX
|
||||
SUBQ AX, DI
|
||||
MOVQ DI, d+48(FP)
|
||||
RET
|
||||
+238
@@ -0,0 +1,238 @@
|
||||
// Copyright 2016 The Snappy-Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build !amd64 appengine !gc noasm
|
||||
|
||||
package snappy
|
||||
|
||||
func load32(b []byte, i int) uint32 {
|
||||
b = b[i : i+4 : len(b)] // Help the compiler eliminate bounds checks on the next line.
|
||||
return uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24
|
||||
}
|
||||
|
||||
func load64(b []byte, i int) uint64 {
|
||||
b = b[i : i+8 : len(b)] // Help the compiler eliminate bounds checks on the next line.
|
||||
return uint64(b[0]) | uint64(b[1])<<8 | uint64(b[2])<<16 | uint64(b[3])<<24 |
|
||||
uint64(b[4])<<32 | uint64(b[5])<<40 | uint64(b[6])<<48 | uint64(b[7])<<56
|
||||
}
|
||||
|
||||
// emitLiteral writes a literal chunk and returns the number of bytes written.
|
||||
//
|
||||
// It assumes that:
|
||||
// dst is long enough to hold the encoded bytes
|
||||
// 1 <= len(lit) && len(lit) <= 65536
|
||||
func emitLiteral(dst, lit []byte) int {
|
||||
i, n := 0, uint(len(lit)-1)
|
||||
switch {
|
||||
case n < 60:
|
||||
dst[0] = uint8(n)<<2 | tagLiteral
|
||||
i = 1
|
||||
case n < 1<<8:
|
||||
dst[0] = 60<<2 | tagLiteral
|
||||
dst[1] = uint8(n)
|
||||
i = 2
|
||||
default:
|
||||
dst[0] = 61<<2 | tagLiteral
|
||||
dst[1] = uint8(n)
|
||||
dst[2] = uint8(n >> 8)
|
||||
i = 3
|
||||
}
|
||||
return i + copy(dst[i:], lit)
|
||||
}
|
||||
|
||||
// emitCopy writes a copy chunk and returns the number of bytes written.
|
||||
//
|
||||
// It assumes that:
|
||||
// dst is long enough to hold the encoded bytes
|
||||
// 1 <= offset && offset <= 65535
|
||||
// 4 <= length && length <= 65535
|
||||
func emitCopy(dst []byte, offset, length int) int {
|
||||
i := 0
|
||||
// The maximum length for a single tagCopy1 or tagCopy2 op is 64 bytes. The
|
||||
// threshold for this loop is a little higher (at 68 = 64 + 4), and the
|
||||
// length emitted down below is is a little lower (at 60 = 64 - 4), because
|
||||
// it's shorter to encode a length 67 copy as a length 60 tagCopy2 followed
|
||||
// by a length 7 tagCopy1 (which encodes as 3+2 bytes) than to encode it as
|
||||
// a length 64 tagCopy2 followed by a length 3 tagCopy2 (which encodes as
|
||||
// 3+3 bytes). The magic 4 in the 64±4 is because the minimum length for a
|
||||
// tagCopy1 op is 4 bytes, which is why a length 3 copy has to be an
|
||||
// encodes-as-3-bytes tagCopy2 instead of an encodes-as-2-bytes tagCopy1.
|
||||
for length >= 68 {
|
||||
// Emit a length 64 copy, encoded as 3 bytes.
|
||||
dst[i+0] = 63<<2 | tagCopy2
|
||||
dst[i+1] = uint8(offset)
|
||||
dst[i+2] = uint8(offset >> 8)
|
||||
i += 3
|
||||
length -= 64
|
||||
}
|
||||
if length > 64 {
|
||||
// Emit a length 60 copy, encoded as 3 bytes.
|
||||
dst[i+0] = 59<<2 | tagCopy2
|
||||
dst[i+1] = uint8(offset)
|
||||
dst[i+2] = uint8(offset >> 8)
|
||||
i += 3
|
||||
length -= 60
|
||||
}
|
||||
if length >= 12 || offset >= 2048 {
|
||||
// Emit the remaining copy, encoded as 3 bytes.
|
||||
dst[i+0] = uint8(length-1)<<2 | tagCopy2
|
||||
dst[i+1] = uint8(offset)
|
||||
dst[i+2] = uint8(offset >> 8)
|
||||
return i + 3
|
||||
}
|
||||
// Emit the remaining copy, encoded as 2 bytes.
|
||||
dst[i+0] = uint8(offset>>8)<<5 | uint8(length-4)<<2 | tagCopy1
|
||||
dst[i+1] = uint8(offset)
|
||||
return i + 2
|
||||
}
|
||||
|
||||
// extendMatch returns the largest k such that k <= len(src) and that
|
||||
// src[i:i+k-j] and src[j:k] have the same contents.
|
||||
//
|
||||
// It assumes that:
|
||||
// 0 <= i && i < j && j <= len(src)
|
||||
func extendMatch(src []byte, i, j int) int {
|
||||
for ; j < len(src) && src[i] == src[j]; i, j = i+1, j+1 {
|
||||
}
|
||||
return j
|
||||
}
|
||||
|
||||
func hash(u, shift uint32) uint32 {
|
||||
return (u * 0x1e35a7bd) >> shift
|
||||
}
|
||||
|
||||
// encodeBlock encodes a non-empty src to a guaranteed-large-enough dst. It
|
||||
// assumes that the varint-encoded length of the decompressed bytes has already
|
||||
// been written.
|
||||
//
|
||||
// It also assumes that:
|
||||
// len(dst) >= MaxEncodedLen(len(src)) &&
|
||||
// minNonLiteralBlockSize <= len(src) && len(src) <= maxBlockSize
|
||||
func encodeBlock(dst, src []byte) (d int) {
|
||||
// Initialize the hash table. Its size ranges from 1<<8 to 1<<14 inclusive.
|
||||
// The table element type is uint16, as s < sLimit and sLimit < len(src)
|
||||
// and len(src) <= maxBlockSize and maxBlockSize == 65536.
|
||||
const (
|
||||
maxTableSize = 1 << 14
|
||||
// tableMask is redundant, but helps the compiler eliminate bounds
|
||||
// checks.
|
||||
tableMask = maxTableSize - 1
|
||||
)
|
||||
shift := uint32(32 - 8)
|
||||
for tableSize := 1 << 8; tableSize < maxTableSize && tableSize < len(src); tableSize *= 2 {
|
||||
shift--
|
||||
}
|
||||
// In Go, all array elements are zero-initialized, so there is no advantage
|
||||
// to a smaller tableSize per se. However, it matches the C++ algorithm,
|
||||
// and in the asm versions of this code, we can get away with zeroing only
|
||||
// the first tableSize elements.
|
||||
var table [maxTableSize]uint16
|
||||
|
||||
// sLimit is when to stop looking for offset/length copies. The inputMargin
|
||||
// lets us use a fast path for emitLiteral in the main loop, while we are
|
||||
// looking for copies.
|
||||
sLimit := len(src) - inputMargin
|
||||
|
||||
// nextEmit is where in src the next emitLiteral should start from.
|
||||
nextEmit := 0
|
||||
|
||||
// The encoded form must start with a literal, as there are no previous
|
||||
// bytes to copy, so we start looking for hash matches at s == 1.
|
||||
s := 1
|
||||
nextHash := hash(load32(src, s), shift)
|
||||
|
||||
for {
|
||||
// Copied from the C++ snappy implementation:
|
||||
//
|
||||
// Heuristic match skipping: If 32 bytes are scanned with no matches
|
||||
// found, start looking only at every other byte. If 32 more bytes are
|
||||
// scanned (or skipped), look at every third byte, etc.. When a match
|
||||
// is found, immediately go back to looking at every byte. This is a
|
||||
// small loss (~5% performance, ~0.1% density) for compressible data
|
||||
// due to more bookkeeping, but for non-compressible data (such as
|
||||
// JPEG) it's a huge win since the compressor quickly "realizes" the
|
||||
// data is incompressible and doesn't bother looking for matches
|
||||
// everywhere.
|
||||
//
|
||||
// The "skip" variable keeps track of how many bytes there are since
|
||||
// the last match; dividing it by 32 (ie. right-shifting by five) gives
|
||||
// the number of bytes to move ahead for each iteration.
|
||||
skip := 32
|
||||
|
||||
nextS := s
|
||||
candidate := 0
|
||||
for {
|
||||
s = nextS
|
||||
bytesBetweenHashLookups := skip >> 5
|
||||
nextS = s + bytesBetweenHashLookups
|
||||
skip += bytesBetweenHashLookups
|
||||
if nextS > sLimit {
|
||||
goto emitRemainder
|
||||
}
|
||||
candidate = int(table[nextHash&tableMask])
|
||||
table[nextHash&tableMask] = uint16(s)
|
||||
nextHash = hash(load32(src, nextS), shift)
|
||||
if load32(src, s) == load32(src, candidate) {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// A 4-byte match has been found. We'll later see if more than 4 bytes
|
||||
// match. But, prior to the match, src[nextEmit:s] are unmatched. Emit
|
||||
// them as literal bytes.
|
||||
d += emitLiteral(dst[d:], src[nextEmit:s])
|
||||
|
||||
// Call emitCopy, and then see if another emitCopy could be our next
|
||||
// move. Repeat until we find no match for the input immediately after
|
||||
// what was consumed by the last emitCopy call.
|
||||
//
|
||||
// If we exit this loop normally then we need to call emitLiteral next,
|
||||
// though we don't yet know how big the literal will be. We handle that
|
||||
// by proceeding to the next iteration of the main loop. We also can
|
||||
// exit this loop via goto if we get close to exhausting the input.
|
||||
for {
|
||||
// Invariant: we have a 4-byte match at s, and no need to emit any
|
||||
// literal bytes prior to s.
|
||||
base := s
|
||||
|
||||
// Extend the 4-byte match as long as possible.
|
||||
//
|
||||
// This is an inlined version of:
|
||||
// s = extendMatch(src, candidate+4, s+4)
|
||||
s += 4
|
||||
for i := candidate + 4; s < len(src) && src[i] == src[s]; i, s = i+1, s+1 {
|
||||
}
|
||||
|
||||
d += emitCopy(dst[d:], base-candidate, s-base)
|
||||
nextEmit = s
|
||||
if s >= sLimit {
|
||||
goto emitRemainder
|
||||
}
|
||||
|
||||
// We could immediately start working at s now, but to improve
|
||||
// compression we first update the hash table at s-1 and at s. If
|
||||
// another emitCopy is not our next move, also calculate nextHash
|
||||
// at s+1. At least on GOARCH=amd64, these three hash calculations
|
||||
// are faster as one load64 call (with some shifts) instead of
|
||||
// three load32 calls.
|
||||
x := load64(src, s-1)
|
||||
prevHash := hash(uint32(x>>0), shift)
|
||||
table[prevHash&tableMask] = uint16(s - 1)
|
||||
currHash := hash(uint32(x>>8), shift)
|
||||
candidate = int(table[currHash&tableMask])
|
||||
table[currHash&tableMask] = uint16(s)
|
||||
if uint32(x>>8) != load32(src, candidate) {
|
||||
nextHash = hash(uint32(x>>16), shift)
|
||||
s++
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
emitRemainder:
|
||||
if nextEmit < len(src) {
|
||||
d += emitLiteral(dst[d:], src[nextEmit:])
|
||||
}
|
||||
return d
|
||||
}
|
||||
+1
@@ -0,0 +1 @@
|
||||
module github.com/golang/snappy
|
||||
+98
@@ -0,0 +1,98 @@
|
||||
// Copyright 2011 The Snappy-Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package snappy implements the Snappy compression format. It aims for very
|
||||
// high speeds and reasonable compression.
|
||||
//
|
||||
// There are actually two Snappy formats: block and stream. They are related,
|
||||
// but different: trying to decompress block-compressed data as a Snappy stream
|
||||
// will fail, and vice versa. The block format is the Decode and Encode
|
||||
// functions and the stream format is the Reader and Writer types.
|
||||
//
|
||||
// The block format, the more common case, is used when the complete size (the
|
||||
// number of bytes) of the original data is known upfront, at the time
|
||||
// compression starts. The stream format, also known as the framing format, is
|
||||
// for when that isn't always true.
|
||||
//
|
||||
// The canonical, C++ implementation is at https://github.com/google/snappy and
|
||||
// it only implements the block format.
|
||||
package snappy // import "github.com/golang/snappy"
|
||||
|
||||
import (
|
||||
"hash/crc32"
|
||||
)
|
||||
|
||||
/*
|
||||
Each encoded block begins with the varint-encoded length of the decoded data,
|
||||
followed by a sequence of chunks. Chunks begin and end on byte boundaries. The
|
||||
first byte of each chunk is broken into its 2 least and 6 most significant bits
|
||||
called l and m: l ranges in [0, 4) and m ranges in [0, 64). l is the chunk tag.
|
||||
Zero means a literal tag. All other values mean a copy tag.
|
||||
|
||||
For literal tags:
|
||||
- If m < 60, the next 1 + m bytes are literal bytes.
|
||||
- Otherwise, let n be the little-endian unsigned integer denoted by the next
|
||||
m - 59 bytes. The next 1 + n bytes after that are literal bytes.
|
||||
|
||||
For copy tags, length bytes are copied from offset bytes ago, in the style of
|
||||
Lempel-Ziv compression algorithms. In particular:
|
||||
- For l == 1, the offset ranges in [0, 1<<11) and the length in [4, 12).
|
||||
The length is 4 + the low 3 bits of m. The high 3 bits of m form bits 8-10
|
||||
of the offset. The next byte is bits 0-7 of the offset.
|
||||
- For l == 2, the offset ranges in [0, 1<<16) and the length in [1, 65).
|
||||
The length is 1 + m. The offset is the little-endian unsigned integer
|
||||
denoted by the next 2 bytes.
|
||||
- For l == 3, this tag is a legacy format that is no longer issued by most
|
||||
encoders. Nonetheless, the offset ranges in [0, 1<<32) and the length in
|
||||
[1, 65). The length is 1 + m. The offset is the little-endian unsigned
|
||||
integer denoted by the next 4 bytes.
|
||||
*/
|
||||
const (
|
||||
tagLiteral = 0x00
|
||||
tagCopy1 = 0x01
|
||||
tagCopy2 = 0x02
|
||||
tagCopy4 = 0x03
|
||||
)
|
||||
|
||||
const (
|
||||
checksumSize = 4
|
||||
chunkHeaderSize = 4
|
||||
magicChunk = "\xff\x06\x00\x00" + magicBody
|
||||
magicBody = "sNaPpY"
|
||||
|
||||
// maxBlockSize is the maximum size of the input to encodeBlock. It is not
|
||||
// part of the wire format per se, but some parts of the encoder assume
|
||||
// that an offset fits into a uint16.
|
||||
//
|
||||
// Also, for the framing format (Writer type instead of Encode function),
|
||||
// https://github.com/google/snappy/blob/master/framing_format.txt says
|
||||
// that "the uncompressed data in a chunk must be no longer than 65536
|
||||
// bytes".
|
||||
maxBlockSize = 65536
|
||||
|
||||
// maxEncodedLenOfMaxBlockSize equals MaxEncodedLen(maxBlockSize), but is
|
||||
// hard coded to be a const instead of a variable, so that obufLen can also
|
||||
// be a const. Their equivalence is confirmed by
|
||||
// TestMaxEncodedLenOfMaxBlockSize.
|
||||
maxEncodedLenOfMaxBlockSize = 76490
|
||||
|
||||
obufHeaderLen = len(magicChunk) + checksumSize + chunkHeaderSize
|
||||
obufLen = obufHeaderLen + maxEncodedLenOfMaxBlockSize
|
||||
)
|
||||
|
||||
const (
|
||||
chunkTypeCompressedData = 0x00
|
||||
chunkTypeUncompressedData = 0x01
|
||||
chunkTypePadding = 0xfe
|
||||
chunkTypeStreamIdentifier = 0xff
|
||||
)
|
||||
|
||||
var crcTable = crc32.MakeTable(crc32.Castagnoli)
|
||||
|
||||
// crc implements the checksum specified in section 3 of
|
||||
// https://github.com/google/snappy/blob/master/framing_format.txt
|
||||
func crc(b []byte) uint32 {
|
||||
c := crc32.Update(0, crcTable, b)
|
||||
return uint32(c>>15|c<<17) + 0xa282ead8
|
||||
}
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
# Compiled Object files, Static and Dynamic libs (Shared Objects)
|
||||
*.o
|
||||
*.a
|
||||
*.so
|
||||
|
||||
# Folders
|
||||
_obj
|
||||
_test
|
||||
|
||||
# Architecture specific extensions/prefixes
|
||||
*.[568vq]
|
||||
[568vq].out
|
||||
|
||||
*.cgo1.go
|
||||
*.cgo2.c
|
||||
_cgo_defun.c
|
||||
_cgo_gotypes.go
|
||||
_cgo_export.*
|
||||
|
||||
_testmain.go
|
||||
|
||||
*.exe
|
||||
#*
|
||||
*~
|
||||
|
||||
# examples binaries
|
||||
examples/synscan/synscan
|
||||
examples/pfdump/pfdump
|
||||
examples/pcapdump/pcapdump
|
||||
examples/httpassembly/httpassembly
|
||||
examples/statsassembly/statsassembly
|
||||
examples/arpscan/arpscan
|
||||
examples/bidirectional/bidirectional
|
||||
examples/bytediff/bytediff
|
||||
examples/reassemblydump/reassemblydump
|
||||
layers/gen
|
||||
macs/gen
|
||||
pcap/pcap_tester
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
#!/bin/bash
|
||||
|
||||
cd "$(dirname $0)"
|
||||
if [ -n "$(go fmt ./...)" ]; then
|
||||
echo "Go code is not formatted, run 'go fmt github.com/google/stenographer/...'" >&2
|
||||
exit 1
|
||||
fi
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
#!/bin/bash
|
||||
|
||||
cd "$(dirname $0)"
|
||||
|
||||
go get golang.org/x/lint/golint
|
||||
DIRS=". tcpassembly tcpassembly/tcpreader ip4defrag reassembly macs pcapgo pcap afpacket pfring routing defrag/lcmdefrag"
|
||||
# Add subdirectories here as we clean up golint on each.
|
||||
for subdir in $DIRS; do
|
||||
pushd $subdir
|
||||
if golint |
|
||||
grep -v CannotSetRFMon | # pcap exported error name
|
||||
grep -v DataLost | # tcpassembly/tcpreader exported error name
|
||||
grep .; then
|
||||
exit 1
|
||||
fi
|
||||
popd
|
||||
done
|
||||
|
||||
pushd layers
|
||||
for file in *.go; do
|
||||
if cat .lint_blacklist | grep -q $file; then
|
||||
echo "Skipping lint of $file due to .lint_blacklist"
|
||||
elif golint $file | grep .; then
|
||||
echo "Lint error in file $file"
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
popd
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
#!/bin/bash
|
||||
|
||||
cd "$(dirname $0)"
|
||||
DIRS=". layers pcap pcapgo tcpassembly tcpassembly/tcpreader routing ip4defrag bytediff macs defrag/lcmdefrag"
|
||||
set -e
|
||||
for subdir in $DIRS; do
|
||||
pushd $subdir
|
||||
go vet
|
||||
popd
|
||||
done
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -ev
|
||||
|
||||
go get github.com/google/gopacket
|
||||
go get github.com/google/gopacket/layers
|
||||
go get github.com/google/gopacket/tcpassembly
|
||||
go get github.com/google/gopacket/reassembly
|
||||
go get github.com/google/gopacket/pcapgo
|
||||
+10
@@ -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
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
language: go
|
||||
go:
|
||||
- 1.11.x
|
||||
- 1.12.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
|
||||
- os: windows
|
||||
go: 1.x
|
||||
# winpcap does not work on travis ci - so install nmap to get libpcap
|
||||
before_install: choco install nmap
|
||||
- stage: style
|
||||
name: "fmt/vet/lint"
|
||||
go: 1.x
|
||||
script:
|
||||
- ./.travis.gofmt.sh
|
||||
- ./.travis.govet.sh
|
||||
- ./.travis.golint.sh
|
||||
|
||||
stages:
|
||||
- style
|
||||
- test
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
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>
|
||||
|
||||
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>
|
||||
|
||||
-----------------------------------------------
|
||||
FORKED FROM github.com/akrennmair/gopcap
|
||||
ALL THE FOLLOWING ARE FOR THAT PROJECT
|
||||
|
||||
MAIN DEVELOPERS:
|
||||
Andreas Krennmair <ak@synflood.at>
|
||||
|
||||
CONTRIBUTORS:
|
||||
Andrea Nall <anall@andreanall.com>
|
||||
Daniel Arndt <danielarndt@gmail.com>
|
||||
Dustin Sallings <dustin@spy.net>
|
||||
Graeme Connell <gconnell@google.com, gsconnell@gmail.com>
|
||||
Guillaume Savary <guillaume@savary.name>
|
||||
Mark Smith <mark@qq.is>
|
||||
Miek Gieben <miek@miek.nl>
|
||||
Mike Bell <mike@mikebell.org>
|
||||
Trevor Strohman <strohman@google.com>
|
||||
+215
@@ -0,0 +1,215 @@
|
||||
Contributing To gopacket
|
||||
========================
|
||||
|
||||
So you've got some code and you'd like it to be part of gopacket... wonderful!
|
||||
We're happy to accept contributions, whether they're fixes to old protocols, new
|
||||
protocols entirely, or anything else you think would improve the gopacket
|
||||
library. This document is designed to help you to do just that.
|
||||
|
||||
The first section deals with the plumbing: how to actually get a change
|
||||
submitted.
|
||||
|
||||
The second section deals with coding style... Go is great in that it
|
||||
has a uniform style implemented by 'go fmt', but there's still some decisions
|
||||
we've made that go above and beyond, and if you follow them, they won't come up
|
||||
in your code review.
|
||||
|
||||
The third section deals with some of the implementation decisions we've made,
|
||||
which may help you to understand the current code and which we may ask you to
|
||||
conform to (or provide compelling reasons for ignoring).
|
||||
|
||||
Overall, we hope this document will help you to understand our system and write
|
||||
great code which fits in, and help us to turn around on your code review quickly
|
||||
so the code can make it into the master branch as quickly as possible.
|
||||
|
||||
|
||||
How To Submit Code
|
||||
------------------
|
||||
|
||||
We use github.com's Pull Request feature to receive code contributions from
|
||||
external contributors. See
|
||||
https://help.github.com/articles/creating-a-pull-request/ for details on
|
||||
how to create a request.
|
||||
|
||||
Also, there's a local script `gc` in the base directory of GoPacket that
|
||||
runs a local set of checks, which should give you relatively high confidence
|
||||
that your pull won't fail github pull checks.
|
||||
|
||||
```sh
|
||||
go get github.com/google/gopacket
|
||||
cd $GOROOT/src/pkg/github.com/google/gopacket
|
||||
git checkout -b <mynewfeature> # create a new branch to work from
|
||||
... code code code ...
|
||||
./gc # Run this to do local commits, it performs a number of checks
|
||||
```
|
||||
|
||||
To sum up:
|
||||
|
||||
* DO
|
||||
+ Pull down the latest version.
|
||||
+ Make a feature-specific branch.
|
||||
+ Code using the style and methods discussed in the rest of this document.
|
||||
+ Use the ./gc command to do local commits or check correctness.
|
||||
+ Push your new feature branch up to github.com, as a pull request.
|
||||
+ Handle comments and requests from reviewers, pushing new commits up to
|
||||
your feature branch as problems are addressed.
|
||||
+ Put interesting comments and discussions into commit comments.
|
||||
* DON'T
|
||||
+ Push to someone else's branch without their permission.
|
||||
|
||||
|
||||
Coding Style
|
||||
------------
|
||||
|
||||
* Go code must be run through `go fmt`, `go vet`, and `golint`
|
||||
* Follow http://golang.org/doc/effective_go.html as much as possible.
|
||||
+ In particular, http://golang.org/doc/effective_go.html#mixed-caps. Enums
|
||||
should be be CamelCase, with acronyms capitalized (TCPSourcePort, vs.
|
||||
TcpSourcePort or TCP_SOURCE_PORT).
|
||||
* Bonus points for giving enum types a String() field.
|
||||
* Any exported types or functions should have commentary
|
||||
(http://golang.org/doc/effective_go.html#commentary)
|
||||
|
||||
|
||||
Coding Methods And Implementation Notes
|
||||
---------------------------------------
|
||||
|
||||
### Error Handling
|
||||
|
||||
Many times, you'll be decoding a protocol and run across something bad, a packet
|
||||
corruption or the like. How do you handle this? First off, ALWAYS report the
|
||||
error. You can do this either by returning the error from the decode() function
|
||||
(most common), or if you're up for it you can implement and add an ErrorLayer
|
||||
through the packet builder (the first method is a simple shortcut that does
|
||||
exactly this, then stops any future decoding).
|
||||
|
||||
Often, you'll already have decode some part of your protocol by the time you hit
|
||||
your error. Use your own discretion to determine whether the stuff you've
|
||||
already decoded should be returned to the caller or not:
|
||||
|
||||
```go
|
||||
func decodeMyProtocol(data []byte, p gopacket.PacketBuilder) error {
|
||||
prot := &MyProtocol{}
|
||||
if len(data) < 10 {
|
||||
// This error occurred before we did ANYTHING, so there's nothing in my
|
||||
// protocol that the caller could possibly want. Just return the error.
|
||||
return fmt.Errorf("Length %d less than 10", len(data))
|
||||
}
|
||||
prot.ImportantField1 = data[:5]
|
||||
prot.ImportantField2 = data[5:10]
|
||||
// At this point, we've already got enough information in 'prot' to
|
||||
// warrant returning it to the caller, so we'll add it now.
|
||||
p.AddLayer(prot)
|
||||
if len(data) < 15 {
|
||||
// We encountered an error later in the packet, but the caller already
|
||||
// has the important info we've gleaned so far.
|
||||
return fmt.Errorf("Length %d less than 15", len(data))
|
||||
}
|
||||
prot.ImportantField3 = data[10:15]
|
||||
return nil // We've already added the layer, we can just return success.
|
||||
}
|
||||
```
|
||||
|
||||
In general, our code follows the approach of returning the first error it
|
||||
encounters. In general, we don't trust any bytes after the first error we see.
|
||||
|
||||
### What Is A Layer?
|
||||
|
||||
The definition of a layer is up to the discretion of the coder. It should be
|
||||
something important enough that it's actually useful to the caller (IE: every
|
||||
TLV value should probably NOT be a layer). However, it can be more granular
|
||||
than a single protocol... IPv6 and SCTP both implement many layers to handle the
|
||||
various parts of the protocol. Use your best judgement, and prepare to defend
|
||||
your decisions during code review. ;)
|
||||
|
||||
### Performance
|
||||
|
||||
We strive to make gopacket as fast as possible while still providing lots of
|
||||
features. In general, this means:
|
||||
|
||||
* Focus performance tuning on common protocols (IP4/6, TCP, etc), and optimize
|
||||
others on an as-needed basis (tons of MPLS on your network? Time to optimize
|
||||
MPLS!)
|
||||
* Use fast operations. See the toplevel benchmark_test for benchmarks of some
|
||||
of Go's underlying features and types.
|
||||
* Test your performance changes! You should use the ./gc script's --benchmark
|
||||
flag to submit any performance-related changes. Use pcap/gopacket_benchmark
|
||||
to test your change against a PCAP file based on your traffic patterns.
|
||||
* Don't be TOO hacky. Sometimes, removing an unused struct from a field causes
|
||||
a huge performance hit, due to the way that Go currently handles its segmented
|
||||
stack... don't be afraid to clean it up anyway. We'll trust the Go compiler
|
||||
to get good enough over time to handle this. Also, this type of
|
||||
compiler-specific optimization is very fragile; someone adding a field to an
|
||||
entirely different struct elsewhere in the codebase could reverse any gains
|
||||
you might achieve by aligning your allocations.
|
||||
* Try to minimize memory allocations. If possible, use []byte to reference
|
||||
pieces of the input, instead of using string, which requires copying the bytes
|
||||
into a new memory allocation.
|
||||
* Think hard about what should be evaluated lazily vs. not. In general, a
|
||||
layer's struct should almost exactly mirror the layer's frame. Anything
|
||||
that's more interesting should be a function. This may not always be
|
||||
possible, but it's a good rule of thumb.
|
||||
* Don't fear micro-optimizations. With the above in mind, we welcome
|
||||
micro-optimizations that we think will have positive/neutral impacts on the
|
||||
majority of workloads. A prime example of this is pre-allocating certain
|
||||
structs within a larger one:
|
||||
|
||||
```go
|
||||
type MyProtocol struct {
|
||||
// Most packets have 1-4 of VeryCommon, so we preallocate it here.
|
||||
initialAllocation [4]uint32
|
||||
VeryCommon []uint32
|
||||
}
|
||||
|
||||
func decodeMyProtocol(data []byte, p gopacket.PacketBuilder) error {
|
||||
prot := &MyProtocol{}
|
||||
prot.VeryCommon = proto.initialAllocation[:0]
|
||||
for len(data) > 4 {
|
||||
field := binary.BigEndian.Uint32(data[:4])
|
||||
data = data[4:]
|
||||
// Since we're using the underlying initialAllocation, we won't need to
|
||||
// allocate new memory for the following append unless we more than 16
|
||||
// bytes of data, which should be the uncommon case.
|
||||
prot.VeryCommon = append(prot.VeryCommon, field)
|
||||
}
|
||||
p.AddLayer(prot)
|
||||
if len(data) > 0 {
|
||||
return fmt.Errorf("MyProtocol packet has %d bytes left after decoding", len(data))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
```
|
||||
|
||||
### Slices And Data
|
||||
|
||||
If you're pulling a slice from the data you're decoding, don't copy it. Just
|
||||
use the slice itself.
|
||||
|
||||
```go
|
||||
type MyProtocol struct {
|
||||
A, B net.IP
|
||||
}
|
||||
func decodeMyProtocol(data []byte, p gopacket.PacketBuilder) error {
|
||||
p.AddLayer(&MyProtocol{
|
||||
A: data[:4],
|
||||
B: data[4:8],
|
||||
})
|
||||
return nil
|
||||
}
|
||||
```
|
||||
|
||||
The caller has already agreed, by using this library, that they won't modify the
|
||||
set of bytes they pass in to the decoder, or the library has already copied the
|
||||
set of bytes to a read-only location. See DecodeOptions.NoCopy for more
|
||||
information.
|
||||
|
||||
### Enums/Types
|
||||
|
||||
If a protocol has an integer field (uint8, uint16, etc) with a couple of known
|
||||
values that mean something special, make it a type. This allows us to do really
|
||||
nice things like adding a String() function to them, so we can more easily
|
||||
display those to users. Check out layers/enums.go for one example, as well as
|
||||
layers/icmp.go for layer-specific enums.
|
||||
|
||||
When naming things, try for descriptiveness over suscinctness. For example,
|
||||
choose DNSResponseRecord over DNSRR.
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
Copyright (c) 2012 Google, Inc. All rights reserved.
|
||||
Copyright (c) 2009-2011 Andreas Krennmair. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
* Neither the name of Andreas Krennmair, Google, nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
+12
@@ -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.
|
||||
|
||||
[](https://travis-ci.org/google/gopacket)
|
||||
[](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.7 due to x/sys/unix dependencies.
|
||||
|
||||
Originally forked from the gopcap project written by Andreas
|
||||
Krennmair <ak@synflood.at> (http://github.com/akrennmair/gopcap).
|
||||
+178
@@ -0,0 +1,178 @@
|
||||
// Copyright 2012 Google, Inc. All rights reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the LICENSE file in the root of the source
|
||||
// tree.
|
||||
|
||||
package gopacket
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// Layer represents a single decoded packet layer (using either the
|
||||
// OSI or TCP/IP definition of a layer). When decoding, a packet's data is
|
||||
// broken up into a number of layers. The caller may call LayerType() to
|
||||
// figure out which type of layer they've received from the packet. Optionally,
|
||||
// they may then use a type assertion to get the actual layer type for deep
|
||||
// inspection of the data.
|
||||
type Layer interface {
|
||||
// LayerType is the gopacket type for this layer.
|
||||
LayerType() LayerType
|
||||
// LayerContents returns the set of bytes that make up this layer.
|
||||
LayerContents() []byte
|
||||
// LayerPayload returns the set of bytes contained within this layer, not
|
||||
// including the layer itself.
|
||||
LayerPayload() []byte
|
||||
}
|
||||
|
||||
// Payload is a Layer containing the payload of a packet. The definition of
|
||||
// what constitutes the payload of a packet depends on previous layers; for
|
||||
// TCP and UDP, we stop decoding above layer 4 and return the remaining
|
||||
// bytes as a Payload. Payload is an ApplicationLayer.
|
||||
type Payload []byte
|
||||
|
||||
// LayerType returns LayerTypePayload
|
||||
func (p Payload) LayerType() LayerType { return LayerTypePayload }
|
||||
|
||||
// LayerContents returns the bytes making up this layer.
|
||||
func (p Payload) LayerContents() []byte { return []byte(p) }
|
||||
|
||||
// LayerPayload returns the payload within this layer.
|
||||
func (p Payload) LayerPayload() []byte { return nil }
|
||||
|
||||
// Payload returns this layer as bytes.
|
||||
func (p Payload) Payload() []byte { return []byte(p) }
|
||||
|
||||
// String implements fmt.Stringer.
|
||||
func (p Payload) String() string { return fmt.Sprintf("%d byte(s)", len(p)) }
|
||||
|
||||
// GoString implements fmt.GoStringer.
|
||||
func (p Payload) GoString() string { return LongBytesGoString([]byte(p)) }
|
||||
|
||||
// CanDecode implements DecodingLayer.
|
||||
func (p Payload) CanDecode() LayerClass { return LayerTypePayload }
|
||||
|
||||
// NextLayerType implements DecodingLayer.
|
||||
func (p Payload) NextLayerType() LayerType { return LayerTypeZero }
|
||||
|
||||
// DecodeFromBytes implements DecodingLayer.
|
||||
func (p *Payload) DecodeFromBytes(data []byte, df DecodeFeedback) error {
|
||||
*p = Payload(data)
|
||||
return nil
|
||||
}
|
||||
|
||||
// SerializeTo writes the serialized form of this layer into the
|
||||
// SerializationBuffer, implementing gopacket.SerializableLayer.
|
||||
// See the docs for gopacket.SerializableLayer for more info.
|
||||
func (p Payload) SerializeTo(b SerializeBuffer, opts SerializeOptions) error {
|
||||
bytes, err := b.PrependBytes(len(p))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
copy(bytes, p)
|
||||
return nil
|
||||
}
|
||||
|
||||
// decodePayload decodes data by returning it all in a Payload layer.
|
||||
func decodePayload(data []byte, p PacketBuilder) error {
|
||||
payload := &Payload{}
|
||||
if err := payload.DecodeFromBytes(data, p); err != nil {
|
||||
return nil
|
||||
}
|
||||
p.AddLayer(payload)
|
||||
p.SetApplicationLayer(payload)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Fragment is a Layer containing a fragment of a larger frame, used by layers
|
||||
// like IPv4 and IPv6 that allow for fragmentation of their payloads.
|
||||
type Fragment []byte
|
||||
|
||||
// LayerType returns LayerTypeFragment
|
||||
func (p *Fragment) LayerType() LayerType { return LayerTypeFragment }
|
||||
|
||||
// LayerContents implements Layer.
|
||||
func (p *Fragment) LayerContents() []byte { return []byte(*p) }
|
||||
|
||||
// LayerPayload implements Layer.
|
||||
func (p *Fragment) LayerPayload() []byte { return nil }
|
||||
|
||||
// Payload returns this layer as a byte slice.
|
||||
func (p *Fragment) Payload() []byte { return []byte(*p) }
|
||||
|
||||
// String implements fmt.Stringer.
|
||||
func (p *Fragment) String() string { return fmt.Sprintf("%d byte(s)", len(*p)) }
|
||||
|
||||
// CanDecode implements DecodingLayer.
|
||||
func (p *Fragment) CanDecode() LayerClass { return LayerTypeFragment }
|
||||
|
||||
// NextLayerType implements DecodingLayer.
|
||||
func (p *Fragment) NextLayerType() LayerType { return LayerTypeZero }
|
||||
|
||||
// DecodeFromBytes implements DecodingLayer.
|
||||
func (p *Fragment) DecodeFromBytes(data []byte, df DecodeFeedback) error {
|
||||
*p = Fragment(data)
|
||||
return nil
|
||||
}
|
||||
|
||||
// SerializeTo writes the serialized form of this layer into the
|
||||
// SerializationBuffer, implementing gopacket.SerializableLayer.
|
||||
// See the docs for gopacket.SerializableLayer for more info.
|
||||
func (p *Fragment) SerializeTo(b SerializeBuffer, opts SerializeOptions) error {
|
||||
bytes, err := b.PrependBytes(len(*p))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
copy(bytes, *p)
|
||||
return nil
|
||||
}
|
||||
|
||||
// decodeFragment decodes data by returning it all in a Fragment layer.
|
||||
func decodeFragment(data []byte, p PacketBuilder) error {
|
||||
payload := &Fragment{}
|
||||
if err := payload.DecodeFromBytes(data, p); err != nil {
|
||||
return nil
|
||||
}
|
||||
p.AddLayer(payload)
|
||||
p.SetApplicationLayer(payload)
|
||||
return nil
|
||||
}
|
||||
|
||||
// These layers correspond to Internet Protocol Suite (TCP/IP) layers, and their
|
||||
// corresponding OSI layers, as best as possible.
|
||||
|
||||
// LinkLayer is the packet layer corresponding to TCP/IP layer 1 (OSI layer 2)
|
||||
type LinkLayer interface {
|
||||
Layer
|
||||
LinkFlow() Flow
|
||||
}
|
||||
|
||||
// NetworkLayer is the packet layer corresponding to TCP/IP layer 2 (OSI
|
||||
// layer 3)
|
||||
type NetworkLayer interface {
|
||||
Layer
|
||||
NetworkFlow() Flow
|
||||
}
|
||||
|
||||
// TransportLayer is the packet layer corresponding to the TCP/IP layer 3 (OSI
|
||||
// layer 4)
|
||||
type TransportLayer interface {
|
||||
Layer
|
||||
TransportFlow() Flow
|
||||
}
|
||||
|
||||
// ApplicationLayer is the packet layer corresponding to the TCP/IP layer 4 (OSI
|
||||
// layer 7), also known as the packet payload.
|
||||
type ApplicationLayer interface {
|
||||
Layer
|
||||
Payload() []byte
|
||||
}
|
||||
|
||||
// ErrorLayer is a packet layer created when decoding of the packet has failed.
|
||||
// Its payload is all the bytes that we were unable to decode, and the returned
|
||||
// error details why the decoding failed.
|
||||
type ErrorLayer interface {
|
||||
Layer
|
||||
Error() error
|
||||
}
|
||||
+157
@@ -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")
|
||||
}
|
||||
+371
@@ -0,0 +1,371 @@
|
||||
// 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.NewFlow(layers.NewUDPPortEndpoint(1000), layers.NewUDPPortEndpoint(500))
|
||||
if t := packet.NetworkLayer(); t != nil && t.TransportFlow() == interestingFlow {
|
||||
fmt.Println("Found that UDP flow I was looking for!")
|
||||
}
|
||||
|
||||
For load-balancing purposes, both Flow and Endpoint have FastHash() functions,
|
||||
which provide quick, non-cryptographic hashes of their contents. Of particular
|
||||
importance is the fact that Flow FastHash() is symmetric: A->B will have the same
|
||||
hash as B->A. An example usage could be:
|
||||
|
||||
channels := [8]chan gopacket.Packet
|
||||
for i := 0; i < 8; i++ {
|
||||
channels[i] = make(chan gopacket.Packet)
|
||||
go packetHandler(channels[i])
|
||||
}
|
||||
for packet := range getPackets() {
|
||||
if net := packet.NetworkLayer(); net != nil {
|
||||
channels[int(net.NetworkFlow().FastHash()) & 0x7] <- packet
|
||||
}
|
||||
}
|
||||
|
||||
This allows us to split up a packet stream while still making sure that each
|
||||
stream sees all packets for a flow (and its bidirectional opposite).
|
||||
|
||||
|
||||
Implementing Your Own Decoder
|
||||
|
||||
If your network has some strange encapsulation, you can implement your own
|
||||
decoder. In this example, we handle Ethernet packets which are encapsulated
|
||||
in a 4-byte header.
|
||||
|
||||
// Create a layer type, should be unique and high, so it doesn't conflict,
|
||||
// giving it a name and a decoder to use.
|
||||
var MyLayerType = gopacket.RegisterLayerType(12345, 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.
|
||||
|
||||
|
||||
Creating Packet Data
|
||||
|
||||
As well as offering the ability to decode packet data, gopacket will allow you
|
||||
to create packets from scratch, as well. A number of gopacket layers implement
|
||||
the SerializableLayer interface; these layers can be serialized to a []byte in
|
||||
the following manner:
|
||||
|
||||
ip := &layers.IPv4{
|
||||
SrcIP: net.IP{1, 2, 3, 4},
|
||||
DstIP: net.IP{5, 6, 7, 8},
|
||||
// etc...
|
||||
}
|
||||
buf := gopacket.NewSerializeBuffer()
|
||||
opts := gopacket.SerializeOptions{} // See SerializeOptions for more details.
|
||||
err := ip.SerializeTo(buf, opts)
|
||||
if err != nil { panic(err) }
|
||||
fmt.Println(buf.Bytes()) // prints out a byte slice containing the serialized IPv4 layer.
|
||||
|
||||
SerializeTo PREPENDS the given layer onto the SerializeBuffer, and they treat
|
||||
the current buffer's Bytes() slice as the payload of the serializing layer.
|
||||
Therefore, you can serialize an entire packet by serializing a set of layers in
|
||||
reverse order (Payload, then TCP, then IP, then Ethernet, for example). The
|
||||
SerializeBuffer's SerializeLayers function is a helper that does exactly that.
|
||||
|
||||
To generate a (empty and useless, because no fields are set)
|
||||
Ethernet(IPv4(TCP(Payload))) packet, for example, you can run:
|
||||
|
||||
buf := gopacket.NewSerializeBuffer()
|
||||
opts := gopacket.SerializeOptions{}
|
||||
gopacket.SerializeLayers(buf, opts,
|
||||
&layers.Ethernet{},
|
||||
&layers.IPv4{},
|
||||
&layers.TCP{},
|
||||
gopacket.Payload([]byte{1, 2, 3, 4}))
|
||||
packetData := buf.Bytes()
|
||||
|
||||
A Final Note
|
||||
|
||||
If you use gopacket, you'll almost definitely want to make sure gopacket/layers
|
||||
is imported, since when imported it sets all the LayerType variables and fills
|
||||
in a lot of interesting variables/maps (DecodersByLayerName, etc). Therefore,
|
||||
it's recommended that even if you don't use any layers functions directly, you still import with:
|
||||
|
||||
import (
|
||||
_ "github.com/google/gopacket/layers"
|
||||
)
|
||||
*/
|
||||
package gopacket
|
||||
+236
@@ -0,0 +1,236 @@
|
||||
// Copyright 2012 Google, Inc. All rights reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the LICENSE file in the root of the source
|
||||
// tree.
|
||||
|
||||
package gopacket
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// MaxEndpointSize determines the maximum size in bytes of an endpoint address.
|
||||
//
|
||||
// Endpoints/Flows have a problem: They need to be hashable. Therefore, they
|
||||
// can't use a byte slice. The two obvious choices are to use a string or a
|
||||
// byte array. Strings work great, but string creation requires memory
|
||||
// allocation, which can be slow. Arrays work great, but have a fixed size. We
|
||||
// originally used the former, now we've switched to the latter. Use of a fixed
|
||||
// byte-array doubles the speed of constructing a flow (due to not needing to
|
||||
// allocate). This is a huge increase... too much for us to pass up.
|
||||
//
|
||||
// The end result of this, though, is that an endpoint/flow can't be created
|
||||
// using more than MaxEndpointSize bytes per address.
|
||||
const MaxEndpointSize = 16
|
||||
|
||||
// Endpoint is the set of bytes used to address packets at various layers.
|
||||
// See LinkLayer, NetworkLayer, and TransportLayer specifications.
|
||||
// Endpoints are usable as map keys.
|
||||
type Endpoint struct {
|
||||
typ EndpointType
|
||||
len int
|
||||
raw [MaxEndpointSize]byte
|
||||
}
|
||||
|
||||
// EndpointType returns the endpoint type associated with this endpoint.
|
||||
func (a Endpoint) EndpointType() EndpointType { return a.typ }
|
||||
|
||||
// Raw returns the raw bytes of this endpoint. These aren't human-readable
|
||||
// most of the time, but they are faster than calling String.
|
||||
func (a Endpoint) Raw() []byte { return a.raw[:a.len] }
|
||||
|
||||
// LessThan provides a stable ordering for all endpoints. It sorts first based
|
||||
// on the EndpointType of an endpoint, then based on the raw bytes of that
|
||||
// endpoint.
|
||||
//
|
||||
// For some endpoints, the actual comparison may not make sense, however this
|
||||
// ordering does provide useful information for most Endpoint types.
|
||||
// Ordering is based first on endpoint type, then on raw endpoint bytes.
|
||||
// Endpoint bytes are sorted lexicographically.
|
||||
func (a Endpoint) LessThan(b Endpoint) bool {
|
||||
return a.typ < b.typ || (a.typ == b.typ && bytes.Compare(a.raw[:a.len], b.raw[:b.len]) < 0)
|
||||
}
|
||||
|
||||
// fnvHash is used by our FastHash functions, and implements the FNV hash
|
||||
// created by Glenn Fowler, Landon Curt Noll, and Phong Vo.
|
||||
// See http://isthe.com/chongo/tech/comp/fnv/.
|
||||
func fnvHash(s []byte) (h uint64) {
|
||||
h = fnvBasis
|
||||
for i := 0; i < len(s); i++ {
|
||||
h ^= uint64(s[i])
|
||||
h *= fnvPrime
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
const fnvBasis = 14695981039346656037
|
||||
const fnvPrime = 1099511628211
|
||||
|
||||
// FastHash provides a quick hashing function for an endpoint, useful if you'd
|
||||
// like to split up endpoints by modulos or other load-balancing techniques.
|
||||
// It uses a variant of Fowler-Noll-Vo hashing.
|
||||
//
|
||||
// The output of FastHash is not guaranteed to remain the same through future
|
||||
// code revisions, so should not be used to key values in persistent storage.
|
||||
func (a Endpoint) FastHash() (h uint64) {
|
||||
h = fnvHash(a.raw[:a.len])
|
||||
h ^= uint64(a.typ)
|
||||
h *= fnvPrime
|
||||
return
|
||||
}
|
||||
|
||||
// NewEndpoint creates a new Endpoint object.
|
||||
//
|
||||
// The size of raw must be less than MaxEndpointSize, otherwise this function
|
||||
// will panic.
|
||||
func NewEndpoint(typ EndpointType, raw []byte) (e Endpoint) {
|
||||
e.len = len(raw)
|
||||
if e.len > MaxEndpointSize {
|
||||
panic("raw byte length greater than MaxEndpointSize")
|
||||
}
|
||||
e.typ = typ
|
||||
copy(e.raw[:], raw)
|
||||
return
|
||||
}
|
||||
|
||||
// EndpointTypeMetadata is used to register a new endpoint type.
|
||||
type EndpointTypeMetadata struct {
|
||||
// Name is the string returned by an EndpointType's String function.
|
||||
Name string
|
||||
// Formatter is called from an Endpoint's String function to format the raw
|
||||
// bytes in an Endpoint into a human-readable string.
|
||||
Formatter func([]byte) string
|
||||
}
|
||||
|
||||
// EndpointType is the type of a gopacket Endpoint. This type determines how
|
||||
// the bytes stored in the endpoint should be interpreted.
|
||||
type EndpointType int64
|
||||
|
||||
var endpointTypes = map[EndpointType]EndpointTypeMetadata{}
|
||||
|
||||
// RegisterEndpointType creates a new EndpointType and registers it globally.
|
||||
// It MUST be passed a unique number, or it will panic. Numbers 0-999 are
|
||||
// reserved for gopacket's use.
|
||||
func RegisterEndpointType(num int, meta EndpointTypeMetadata) EndpointType {
|
||||
t := EndpointType(num)
|
||||
if _, ok := endpointTypes[t]; ok {
|
||||
panic("Endpoint type number already in use")
|
||||
}
|
||||
endpointTypes[t] = meta
|
||||
return t
|
||||
}
|
||||
|
||||
func (e EndpointType) String() string {
|
||||
if t, ok := endpointTypes[e]; ok {
|
||||
return t.Name
|
||||
}
|
||||
return strconv.Itoa(int(e))
|
||||
}
|
||||
|
||||
func (a Endpoint) String() string {
|
||||
if t, ok := endpointTypes[a.typ]; ok && t.Formatter != nil {
|
||||
return t.Formatter(a.raw[:a.len])
|
||||
}
|
||||
return fmt.Sprintf("%v:%v", a.typ, a.raw)
|
||||
}
|
||||
|
||||
// Flow represents the direction of traffic for a packet layer, as a source and destination Endpoint.
|
||||
// Flows are usable as map keys.
|
||||
type Flow struct {
|
||||
typ EndpointType
|
||||
slen, dlen int
|
||||
src, dst [MaxEndpointSize]byte
|
||||
}
|
||||
|
||||
// FlowFromEndpoints creates a new flow by pasting together two endpoints.
|
||||
// The endpoints must have the same EndpointType, or this function will return
|
||||
// an error.
|
||||
func FlowFromEndpoints(src, dst Endpoint) (_ Flow, err error) {
|
||||
if src.typ != dst.typ {
|
||||
err = fmt.Errorf("Mismatched endpoint types: %v->%v", src.typ, dst.typ)
|
||||
return
|
||||
}
|
||||
return Flow{src.typ, src.len, dst.len, src.raw, dst.raw}, nil
|
||||
}
|
||||
|
||||
// FastHash provides a quick hashing function for a flow, useful if you'd
|
||||
// like to split up flows by modulos or other load-balancing techniques.
|
||||
// It uses a variant of Fowler-Noll-Vo hashing, and is guaranteed to collide
|
||||
// with its reverse flow. IE: the flow A->B will have the same hash as the flow
|
||||
// B->A.
|
||||
//
|
||||
// The output of FastHash is not guaranteed to remain the same through future
|
||||
// code revisions, so should not be used to key values in persistent storage.
|
||||
func (f Flow) FastHash() (h uint64) {
|
||||
// This combination must be commutative. We don't use ^, since that would
|
||||
// give the same hash for all A->A flows.
|
||||
h = fnvHash(f.src[:f.slen]) + fnvHash(f.dst[:f.dlen])
|
||||
h ^= uint64(f.typ)
|
||||
h *= fnvPrime
|
||||
return
|
||||
}
|
||||
|
||||
// String returns a human-readable representation of this flow, in the form
|
||||
// "Src->Dst"
|
||||
func (f Flow) String() string {
|
||||
s, d := f.Endpoints()
|
||||
return fmt.Sprintf("%v->%v", s, d)
|
||||
}
|
||||
|
||||
// EndpointType returns the EndpointType for this Flow.
|
||||
func (f Flow) EndpointType() EndpointType {
|
||||
return f.typ
|
||||
}
|
||||
|
||||
// Endpoints returns the two Endpoints for this flow.
|
||||
func (f Flow) Endpoints() (src, dst Endpoint) {
|
||||
return Endpoint{f.typ, f.slen, f.src}, Endpoint{f.typ, f.dlen, f.dst}
|
||||
}
|
||||
|
||||
// Src returns the source Endpoint for this flow.
|
||||
func (f Flow) Src() (src Endpoint) {
|
||||
src, _ = f.Endpoints()
|
||||
return
|
||||
}
|
||||
|
||||
// Dst returns the destination Endpoint for this flow.
|
||||
func (f Flow) Dst() (dst Endpoint) {
|
||||
_, dst = f.Endpoints()
|
||||
return
|
||||
}
|
||||
|
||||
// Reverse returns a new flow with endpoints reversed.
|
||||
func (f Flow) Reverse() Flow {
|
||||
return Flow{f.typ, f.dlen, f.slen, f.dst, f.src}
|
||||
}
|
||||
|
||||
// NewFlow creates a new flow.
|
||||
//
|
||||
// src and dst must have length <= MaxEndpointSize, otherwise NewFlow will
|
||||
// panic.
|
||||
func NewFlow(t EndpointType, src, dst []byte) (f Flow) {
|
||||
f.slen = len(src)
|
||||
f.dlen = len(dst)
|
||||
if f.slen > MaxEndpointSize || f.dlen > MaxEndpointSize {
|
||||
panic("flow raw byte length greater than MaxEndpointSize")
|
||||
}
|
||||
f.typ = t
|
||||
copy(f.src[:], src)
|
||||
copy(f.dst[:], dst)
|
||||
return
|
||||
}
|
||||
|
||||
// EndpointInvalid is an endpoint type used for invalid endpoints, IE endpoints
|
||||
// that are specified incorrectly during creation.
|
||||
var EndpointInvalid = RegisterEndpointType(0, EndpointTypeMetadata{Name: "invalid", Formatter: func(b []byte) string {
|
||||
return fmt.Sprintf("%v", b)
|
||||
}})
|
||||
|
||||
// InvalidEndpoint is a singleton Endpoint of type EndpointInvalid.
|
||||
var InvalidEndpoint = NewEndpoint(EndpointInvalid, nil)
|
||||
|
||||
// InvalidFlow is a singleton Flow of type EndpointInvalid.
|
||||
var InvalidFlow = NewFlow(EndpointInvalid, nil, nil)
|
||||
+288
@@ -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
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
module github.com/google/gopacket
|
||||
|
||||
go 1.12
|
||||
|
||||
require (
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3
|
||||
golang.org/x/sys v0.0.0-20190405154228-4b34438f7a67
|
||||
)
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3 h1:0GoQqolDA55aaLxZyTzK/Y2ePZzZTUrRacwib7cNsYQ=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190405154228-4b34438f7a67 h1:1Fzlr8kkDLQwqMP8GxrhptBLqZG/EDpiATneiZHY998=
|
||||
golang.org/x/sys v0.0.0-20190405154228-4b34438f7a67/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
+107
@@ -0,0 +1,107 @@
|
||||
// Copyright 2012 Google, Inc. All rights reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the LICENSE file in the root of the source
|
||||
// tree.
|
||||
|
||||
package gopacket
|
||||
|
||||
// LayerClass is a set of LayerTypes, used for grabbing one of a number of
|
||||
// different types from a packet.
|
||||
type LayerClass interface {
|
||||
// Contains returns true if the given layer type should be considered part
|
||||
// of this layer class.
|
||||
Contains(LayerType) bool
|
||||
// LayerTypes returns the set of all layer types in this layer class.
|
||||
// Note that this may not be a fast operation on all LayerClass
|
||||
// implementations.
|
||||
LayerTypes() []LayerType
|
||||
}
|
||||
|
||||
// Contains implements LayerClass.
|
||||
func (l LayerType) Contains(a LayerType) bool {
|
||||
return l == a
|
||||
}
|
||||
|
||||
// LayerTypes implements LayerClass.
|
||||
func (l LayerType) LayerTypes() []LayerType {
|
||||
return []LayerType{l}
|
||||
}
|
||||
|
||||
// LayerClassSlice implements a LayerClass with a slice.
|
||||
type LayerClassSlice []bool
|
||||
|
||||
// Contains returns true if the given layer type should be considered part
|
||||
// of this layer class.
|
||||
func (s LayerClassSlice) Contains(t LayerType) bool {
|
||||
return int(t) < len(s) && s[t]
|
||||
}
|
||||
|
||||
// LayerTypes returns all layer types in this LayerClassSlice.
|
||||
// Because of LayerClassSlice's implementation, this could be quite slow.
|
||||
func (s LayerClassSlice) LayerTypes() (all []LayerType) {
|
||||
for i := 0; i < len(s); i++ {
|
||||
if s[i] {
|
||||
all = append(all, LayerType(i))
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// NewLayerClassSlice creates a new LayerClassSlice by creating a slice of
|
||||
// size max(types) and setting slice[t] to true for each type t. Note, if
|
||||
// you implement your own LayerType and give it a high value, this WILL create
|
||||
// a very large slice.
|
||||
func NewLayerClassSlice(types []LayerType) LayerClassSlice {
|
||||
var max LayerType
|
||||
for _, typ := range types {
|
||||
if typ > max {
|
||||
max = typ
|
||||
}
|
||||
}
|
||||
t := make([]bool, int(max+1))
|
||||
for _, typ := range types {
|
||||
t[typ] = true
|
||||
}
|
||||
return t
|
||||
}
|
||||
|
||||
// LayerClassMap implements a LayerClass with a map.
|
||||
type LayerClassMap map[LayerType]bool
|
||||
|
||||
// Contains returns true if the given layer type should be considered part
|
||||
// of this layer class.
|
||||
func (m LayerClassMap) Contains(t LayerType) bool {
|
||||
return m[t]
|
||||
}
|
||||
|
||||
// LayerTypes returns all layer types in this LayerClassMap.
|
||||
func (m LayerClassMap) LayerTypes() (all []LayerType) {
|
||||
for t := range m {
|
||||
all = append(all, t)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// NewLayerClassMap creates a LayerClassMap and sets map[t] to true for each
|
||||
// type in types.
|
||||
func NewLayerClassMap(types []LayerType) LayerClassMap {
|
||||
m := LayerClassMap{}
|
||||
for _, typ := range types {
|
||||
m[typ] = true
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
// NewLayerClass creates a LayerClass, attempting to be smart about which type
|
||||
// it creates based on which types are passed in.
|
||||
func NewLayerClass(types []LayerType) LayerClass {
|
||||
for _, typ := range types {
|
||||
if typ > maxLayerType {
|
||||
// NewLayerClassSlice could create a very large object, so instead create
|
||||
// a map.
|
||||
return NewLayerClassMap(types)
|
||||
}
|
||||
}
|
||||
return NewLayerClassSlice(types)
|
||||
}
|
||||
+39
@@ -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
|
||||
+109
@@ -0,0 +1,109 @@
|
||||
// Copyright 2012 Google, Inc. All rights reserved.
|
||||
// Copyright 2009-2011 Andreas Krennmair. All rights reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the LICENSE file in the root of the source
|
||||
// tree.
|
||||
|
||||
package layers
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
|
||||
"github.com/google/gopacket"
|
||||
)
|
||||
|
||||
// Potential values for ARP.Operation.
|
||||
const (
|
||||
ARPRequest = 1
|
||||
ARPReply = 2
|
||||
)
|
||||
|
||||
// ARP is a ARP packet header.
|
||||
type ARP struct {
|
||||
BaseLayer
|
||||
AddrType LinkType
|
||||
Protocol EthernetType
|
||||
HwAddressSize uint8
|
||||
ProtAddressSize uint8
|
||||
Operation uint16
|
||||
SourceHwAddress []byte
|
||||
SourceProtAddress []byte
|
||||
DstHwAddress []byte
|
||||
DstProtAddress []byte
|
||||
}
|
||||
|
||||
// LayerType returns LayerTypeARP
|
||||
func (arp *ARP) LayerType() gopacket.LayerType { return LayerTypeARP }
|
||||
|
||||
// DecodeFromBytes decodes the given bytes into this layer.
|
||||
func (arp *ARP) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
|
||||
arp.AddrType = LinkType(binary.BigEndian.Uint16(data[0:2]))
|
||||
arp.Protocol = EthernetType(binary.BigEndian.Uint16(data[2:4]))
|
||||
arp.HwAddressSize = data[4]
|
||||
arp.ProtAddressSize = data[5]
|
||||
arp.Operation = binary.BigEndian.Uint16(data[6:8])
|
||||
arp.SourceHwAddress = data[8 : 8+arp.HwAddressSize]
|
||||
arp.SourceProtAddress = data[8+arp.HwAddressSize : 8+arp.HwAddressSize+arp.ProtAddressSize]
|
||||
arp.DstHwAddress = data[8+arp.HwAddressSize+arp.ProtAddressSize : 8+2*arp.HwAddressSize+arp.ProtAddressSize]
|
||||
arp.DstProtAddress = data[8+2*arp.HwAddressSize+arp.ProtAddressSize : 8+2*arp.HwAddressSize+2*arp.ProtAddressSize]
|
||||
|
||||
arpLength := 8 + 2*arp.HwAddressSize + 2*arp.ProtAddressSize
|
||||
arp.Contents = data[:arpLength]
|
||||
arp.Payload = data[arpLength:]
|
||||
return nil
|
||||
}
|
||||
|
||||
// SerializeTo writes the serialized form of this layer into the
|
||||
// SerializationBuffer, implementing gopacket.SerializableLayer.
|
||||
// See the docs for gopacket.SerializableLayer for more info.
|
||||
func (arp *ARP) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
|
||||
size := 8 + len(arp.SourceHwAddress) + len(arp.SourceProtAddress) + len(arp.DstHwAddress) + len(arp.DstProtAddress)
|
||||
bytes, err := b.PrependBytes(size)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if opts.FixLengths {
|
||||
if len(arp.SourceHwAddress) != len(arp.DstHwAddress) {
|
||||
return errors.New("mismatched hardware address sizes")
|
||||
}
|
||||
arp.HwAddressSize = uint8(len(arp.SourceHwAddress))
|
||||
if len(arp.SourceProtAddress) != len(arp.DstProtAddress) {
|
||||
return errors.New("mismatched prot address sizes")
|
||||
}
|
||||
arp.ProtAddressSize = uint8(len(arp.SourceProtAddress))
|
||||
}
|
||||
binary.BigEndian.PutUint16(bytes, uint16(arp.AddrType))
|
||||
binary.BigEndian.PutUint16(bytes[2:], uint16(arp.Protocol))
|
||||
bytes[4] = arp.HwAddressSize
|
||||
bytes[5] = arp.ProtAddressSize
|
||||
binary.BigEndian.PutUint16(bytes[6:], arp.Operation)
|
||||
start := 8
|
||||
for _, addr := range [][]byte{
|
||||
arp.SourceHwAddress,
|
||||
arp.SourceProtAddress,
|
||||
arp.DstHwAddress,
|
||||
arp.DstProtAddress,
|
||||
} {
|
||||
copy(bytes[start:], addr)
|
||||
start += len(addr)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// CanDecode returns the set of layer types that this DecodingLayer can decode.
|
||||
func (arp *ARP) CanDecode() gopacket.LayerClass {
|
||||
return LayerTypeARP
|
||||
}
|
||||
|
||||
// NextLayerType returns the layer type contained by this DecodingLayer.
|
||||
func (arp *ARP) NextLayerType() gopacket.LayerType {
|
||||
return gopacket.LayerTypePayload
|
||||
}
|
||||
|
||||
func decodeARP(data []byte, p gopacket.PacketBuilder) error {
|
||||
|
||||
arp := &ARP{}
|
||||
return decodingLayerDecoder(arp, data, p)
|
||||
}
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
// Copyright 2012 Google, Inc. All rights reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the LICENSE file in the root of the source
|
||||
// tree.
|
||||
|
||||
package layers
|
||||
|
||||
import (
|
||||
"github.com/google/gopacket"
|
||||
)
|
||||
|
||||
// BaseLayer is a convenience struct which implements the LayerData and
|
||||
// LayerPayload functions of the Layer interface.
|
||||
type BaseLayer struct {
|
||||
// Contents is the set of bytes that make up this layer. IE: for an
|
||||
// Ethernet packet, this would be the set of bytes making up the
|
||||
// Ethernet frame.
|
||||
Contents []byte
|
||||
// Payload is the set of bytes contained by (but not part of) this
|
||||
// Layer. Again, to take Ethernet as an example, this would be the
|
||||
// set of bytes encapsulated by the Ethernet protocol.
|
||||
Payload []byte
|
||||
}
|
||||
|
||||
// LayerContents returns the bytes of the packet layer.
|
||||
func (b *BaseLayer) LayerContents() []byte { return b.Contents }
|
||||
|
||||
// LayerPayload returns the bytes contained within the packet layer.
|
||||
func (b *BaseLayer) LayerPayload() []byte { return b.Payload }
|
||||
|
||||
type layerDecodingLayer interface {
|
||||
gopacket.Layer
|
||||
DecodeFromBytes([]byte, gopacket.DecodeFeedback) error
|
||||
NextLayerType() gopacket.LayerType
|
||||
}
|
||||
|
||||
func decodingLayerDecoder(d layerDecodingLayer, data []byte, p gopacket.PacketBuilder) error {
|
||||
err := d.DecodeFromBytes(data, p)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
p.AddLayer(d)
|
||||
next := d.NextLayerType()
|
||||
if next == gopacket.LayerTypeZero {
|
||||
return nil
|
||||
}
|
||||
return p.NextDecoder(next)
|
||||
}
|
||||
|
||||
// hacky way to zero out memory... there must be a better way?
|
||||
var lotsOfZeros [1024]byte
|
||||
+481
@@ -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
|
||||
}
|
||||
+651
@@ -0,0 +1,651 @@
|
||||
// Copyright 2012 Google, Inc. All rights reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the LICENSE file in the root of the source
|
||||
// tree.
|
||||
|
||||
// Enum types courtesy of...
|
||||
// http://search.cpan.org/~mchapman/Net-CDP-0.09/lib/Net/CDP.pm
|
||||
// https://code.google.com/p/ladvd/
|
||||
// http://anonsvn.wireshark.org/viewvc/releases/wireshark-1.8.6/epan/dissectors/packet-cdp.c
|
||||
|
||||
package layers
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"net"
|
||||
|
||||
"github.com/google/gopacket"
|
||||
)
|
||||
|
||||
// CDPTLVType is the type of each TLV value in a CiscoDiscovery packet.
|
||||
type CDPTLVType uint16
|
||||
|
||||
// CDPTLVType values.
|
||||
const (
|
||||
CDPTLVDevID CDPTLVType = 0x0001
|
||||
CDPTLVAddress CDPTLVType = 0x0002
|
||||
CDPTLVPortID CDPTLVType = 0x0003
|
||||
CDPTLVCapabilities CDPTLVType = 0x0004
|
||||
CDPTLVVersion CDPTLVType = 0x0005
|
||||
CDPTLVPlatform CDPTLVType = 0x0006
|
||||
CDPTLVIPPrefix CDPTLVType = 0x0007
|
||||
CDPTLVHello CDPTLVType = 0x0008
|
||||
CDPTLVVTPDomain CDPTLVType = 0x0009
|
||||
CDPTLVNativeVLAN CDPTLVType = 0x000a
|
||||
CDPTLVFullDuplex CDPTLVType = 0x000b
|
||||
CDPTLVVLANReply CDPTLVType = 0x000e
|
||||
CDPTLVVLANQuery CDPTLVType = 0x000f
|
||||
CDPTLVPower CDPTLVType = 0x0010
|
||||
CDPTLVMTU CDPTLVType = 0x0011
|
||||
CDPTLVExtendedTrust CDPTLVType = 0x0012
|
||||
CDPTLVUntrustedCOS CDPTLVType = 0x0013
|
||||
CDPTLVSysName CDPTLVType = 0x0014
|
||||
CDPTLVSysOID CDPTLVType = 0x0015
|
||||
CDPTLVMgmtAddresses CDPTLVType = 0x0016
|
||||
CDPTLVLocation CDPTLVType = 0x0017
|
||||
CDPTLVExternalPortID CDPTLVType = 0x0018
|
||||
CDPTLVPowerRequested CDPTLVType = 0x0019
|
||||
CDPTLVPowerAvailable CDPTLVType = 0x001a
|
||||
CDPTLVPortUnidirectional CDPTLVType = 0x001b
|
||||
CDPTLVEnergyWise CDPTLVType = 0x001d
|
||||
CDPTLVSparePairPOE CDPTLVType = 0x001f
|
||||
)
|
||||
|
||||
// CiscoDiscoveryValue is a TLV value inside a CiscoDiscovery packet layer.
|
||||
type CiscoDiscoveryValue struct {
|
||||
Type CDPTLVType
|
||||
Length uint16
|
||||
Value []byte
|
||||
}
|
||||
|
||||
// CiscoDiscovery is a packet layer containing the Cisco Discovery Protocol.
|
||||
// See http://www.cisco.com/univercd/cc/td/doc/product/lan/trsrb/frames.htm#31885
|
||||
type CiscoDiscovery struct {
|
||||
BaseLayer
|
||||
Version byte
|
||||
TTL byte
|
||||
Checksum uint16
|
||||
Values []CiscoDiscoveryValue
|
||||
}
|
||||
|
||||
// CDPCapability is the set of capabilities advertised by a CDP device.
|
||||
type CDPCapability uint32
|
||||
|
||||
// CDPCapability values.
|
||||
const (
|
||||
CDPCapMaskRouter CDPCapability = 0x0001
|
||||
CDPCapMaskTBBridge CDPCapability = 0x0002
|
||||
CDPCapMaskSPBridge CDPCapability = 0x0004
|
||||
CDPCapMaskSwitch CDPCapability = 0x0008
|
||||
CDPCapMaskHost CDPCapability = 0x0010
|
||||
CDPCapMaskIGMPFilter CDPCapability = 0x0020
|
||||
CDPCapMaskRepeater CDPCapability = 0x0040
|
||||
CDPCapMaskPhone CDPCapability = 0x0080
|
||||
CDPCapMaskRemote CDPCapability = 0x0100
|
||||
)
|
||||
|
||||
// CDPCapabilities represents the capabilities of a device
|
||||
type CDPCapabilities struct {
|
||||
L3Router bool
|
||||
TBBridge bool
|
||||
SPBridge bool
|
||||
L2Switch bool
|
||||
IsHost bool
|
||||
IGMPFilter bool
|
||||
L1Repeater bool
|
||||
IsPhone bool
|
||||
RemotelyManaged bool
|
||||
}
|
||||
|
||||
// CDP Power-over-Ethernet values.
|
||||
const (
|
||||
CDPPoEFourWire byte = 0x01
|
||||
CDPPoEPDArch byte = 0x02
|
||||
CDPPoEPDRequest byte = 0x04
|
||||
CDPPoEPSE byte = 0x08
|
||||
)
|
||||
|
||||
// CDPSparePairPoE provides information on PoE.
|
||||
type CDPSparePairPoE struct {
|
||||
PSEFourWire bool // Supported / Not supported
|
||||
PDArchShared bool // Shared / Independent
|
||||
PDRequestOn bool // On / Off
|
||||
PSEOn bool // On / Off
|
||||
}
|
||||
|
||||
// CDPVLANDialogue encapsulates a VLAN Query/Reply
|
||||
type CDPVLANDialogue struct {
|
||||
ID uint8
|
||||
VLAN uint16
|
||||
}
|
||||
|
||||
// CDPPowerDialogue encapsulates a Power Query/Reply
|
||||
type CDPPowerDialogue struct {
|
||||
ID uint16
|
||||
MgmtID uint16
|
||||
Values []uint32
|
||||
}
|
||||
|
||||
// CDPLocation provides location information for a CDP device.
|
||||
type CDPLocation struct {
|
||||
Type uint8 // Undocumented
|
||||
Location string
|
||||
}
|
||||
|
||||
// CDPHello is a Cisco Hello message (undocumented, hence the "Unknown" fields)
|
||||
type CDPHello struct {
|
||||
OUI []byte
|
||||
ProtocolID uint16
|
||||
ClusterMaster net.IP
|
||||
Unknown1 net.IP
|
||||
Version byte
|
||||
SubVersion byte
|
||||
Status byte
|
||||
Unknown2 byte
|
||||
ClusterCommander net.HardwareAddr
|
||||
SwitchMAC net.HardwareAddr
|
||||
Unknown3 byte
|
||||
ManagementVLAN uint16
|
||||
}
|
||||
|
||||
// CDPEnergyWiseSubtype is used within CDP to define TLV values.
|
||||
type CDPEnergyWiseSubtype uint32
|
||||
|
||||
// CDPEnergyWiseSubtype values.
|
||||
const (
|
||||
CDPEnergyWiseRole CDPEnergyWiseSubtype = 0x00000007
|
||||
CDPEnergyWiseDomain CDPEnergyWiseSubtype = 0x00000008
|
||||
CDPEnergyWiseName CDPEnergyWiseSubtype = 0x00000009
|
||||
CDPEnergyWiseReplyTo CDPEnergyWiseSubtype = 0x00000017
|
||||
)
|
||||
|
||||
// CDPEnergyWise is used by CDP to monitor and control power usage.
|
||||
type CDPEnergyWise struct {
|
||||
EncryptedData []byte
|
||||
Unknown1 uint32
|
||||
SequenceNumber uint32
|
||||
ModelNumber string
|
||||
Unknown2 uint16
|
||||
HardwareID string
|
||||
SerialNum string
|
||||
Unknown3 []byte
|
||||
Role string
|
||||
Domain string
|
||||
Name string
|
||||
ReplyUnknown1 []byte
|
||||
ReplyPort []byte
|
||||
ReplyAddress []byte
|
||||
ReplyUnknown2 []byte
|
||||
ReplyUnknown3 []byte
|
||||
}
|
||||
|
||||
// CiscoDiscoveryInfo represents the decoded details for a set of CiscoDiscoveryValues
|
||||
type CiscoDiscoveryInfo struct {
|
||||
BaseLayer
|
||||
CDPHello
|
||||
DeviceID string
|
||||
Addresses []net.IP
|
||||
PortID string
|
||||
Capabilities CDPCapabilities
|
||||
Version string
|
||||
Platform string
|
||||
IPPrefixes []net.IPNet
|
||||
VTPDomain string
|
||||
NativeVLAN uint16
|
||||
FullDuplex bool
|
||||
VLANReply CDPVLANDialogue
|
||||
VLANQuery CDPVLANDialogue
|
||||
PowerConsumption uint16
|
||||
MTU uint32
|
||||
ExtendedTrust uint8
|
||||
UntrustedCOS uint8
|
||||
SysName string
|
||||
SysOID string
|
||||
MgmtAddresses []net.IP
|
||||
Location CDPLocation
|
||||
PowerRequest CDPPowerDialogue
|
||||
PowerAvailable CDPPowerDialogue
|
||||
SparePairPoe CDPSparePairPoE
|
||||
EnergyWise CDPEnergyWise
|
||||
Unknown []CiscoDiscoveryValue
|
||||
}
|
||||
|
||||
// LayerType returns gopacket.LayerTypeCiscoDiscovery.
|
||||
func (c *CiscoDiscovery) LayerType() gopacket.LayerType {
|
||||
return LayerTypeCiscoDiscovery
|
||||
}
|
||||
|
||||
func decodeCiscoDiscovery(data []byte, p gopacket.PacketBuilder) error {
|
||||
c := &CiscoDiscovery{
|
||||
Version: data[0],
|
||||
TTL: data[1],
|
||||
Checksum: binary.BigEndian.Uint16(data[2:4]),
|
||||
}
|
||||
if c.Version != 1 && c.Version != 2 {
|
||||
return fmt.Errorf("Invalid CiscoDiscovery version number %d", c.Version)
|
||||
}
|
||||
var err error
|
||||
c.Values, err = decodeCiscoDiscoveryTLVs(data[4:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.Contents = data[0:4]
|
||||
c.Payload = data[4:]
|
||||
p.AddLayer(c)
|
||||
return p.NextDecoder(gopacket.DecodeFunc(decodeCiscoDiscoveryInfo))
|
||||
}
|
||||
|
||||
// LayerType returns gopacket.LayerTypeCiscoDiscoveryInfo.
|
||||
func (c *CiscoDiscoveryInfo) LayerType() gopacket.LayerType {
|
||||
return LayerTypeCiscoDiscoveryInfo
|
||||
}
|
||||
|
||||
func decodeCiscoDiscoveryTLVs(data []byte) (values []CiscoDiscoveryValue, err error) {
|
||||
for len(data) > 0 {
|
||||
val := CiscoDiscoveryValue{
|
||||
Type: CDPTLVType(binary.BigEndian.Uint16(data[:2])),
|
||||
Length: binary.BigEndian.Uint16(data[2:4]),
|
||||
}
|
||||
if val.Length < 4 {
|
||||
err = fmt.Errorf("Invalid CiscoDiscovery value length %d", val.Length)
|
||||
break
|
||||
}
|
||||
val.Value = data[4:val.Length]
|
||||
values = append(values, val)
|
||||
data = data[val.Length:]
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func decodeCiscoDiscoveryInfo(data []byte, p gopacket.PacketBuilder) error {
|
||||
var err error
|
||||
info := &CiscoDiscoveryInfo{BaseLayer: BaseLayer{Contents: data}}
|
||||
p.AddLayer(info)
|
||||
values, err := decodeCiscoDiscoveryTLVs(data)
|
||||
if err != nil { // Unlikely, as parent decode will fail, but better safe...
|
||||
return err
|
||||
}
|
||||
for _, val := range values {
|
||||
switch val.Type {
|
||||
case CDPTLVDevID:
|
||||
info.DeviceID = string(val.Value)
|
||||
case CDPTLVAddress:
|
||||
if err = checkCDPTLVLen(val, 4); err != nil {
|
||||
return err
|
||||
}
|
||||
info.Addresses, err = decodeAddresses(val.Value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
case CDPTLVPortID:
|
||||
info.PortID = string(val.Value)
|
||||
case CDPTLVCapabilities:
|
||||
if err = checkCDPTLVLen(val, 4); err != nil {
|
||||
return err
|
||||
}
|
||||
val := CDPCapability(binary.BigEndian.Uint32(val.Value[0:4]))
|
||||
info.Capabilities.L3Router = (val&CDPCapMaskRouter > 0)
|
||||
info.Capabilities.TBBridge = (val&CDPCapMaskTBBridge > 0)
|
||||
info.Capabilities.SPBridge = (val&CDPCapMaskSPBridge > 0)
|
||||
info.Capabilities.L2Switch = (val&CDPCapMaskSwitch > 0)
|
||||
info.Capabilities.IsHost = (val&CDPCapMaskHost > 0)
|
||||
info.Capabilities.IGMPFilter = (val&CDPCapMaskIGMPFilter > 0)
|
||||
info.Capabilities.L1Repeater = (val&CDPCapMaskRepeater > 0)
|
||||
info.Capabilities.IsPhone = (val&CDPCapMaskPhone > 0)
|
||||
info.Capabilities.RemotelyManaged = (val&CDPCapMaskRemote > 0)
|
||||
case CDPTLVVersion:
|
||||
info.Version = string(val.Value)
|
||||
case CDPTLVPlatform:
|
||||
info.Platform = string(val.Value)
|
||||
case CDPTLVIPPrefix:
|
||||
v := val.Value
|
||||
l := len(v)
|
||||
if l%5 == 0 && l >= 5 {
|
||||
for len(v) > 0 {
|
||||
_, ipnet, _ := net.ParseCIDR(fmt.Sprintf("%d.%d.%d.%d/%d", v[0], v[1], v[2], v[3], v[4]))
|
||||
info.IPPrefixes = append(info.IPPrefixes, *ipnet)
|
||||
v = v[5:]
|
||||
}
|
||||
} else {
|
||||
return fmt.Errorf("Invalid TLV %v length %d", val.Type, len(val.Value))
|
||||
}
|
||||
case CDPTLVHello:
|
||||
if err = checkCDPTLVLen(val, 32); err != nil {
|
||||
return err
|
||||
}
|
||||
v := val.Value
|
||||
info.CDPHello.OUI = v[0:3]
|
||||
info.CDPHello.ProtocolID = binary.BigEndian.Uint16(v[3:5])
|
||||
info.CDPHello.ClusterMaster = v[5:9]
|
||||
info.CDPHello.Unknown1 = v[9:13]
|
||||
info.CDPHello.Version = v[13]
|
||||
info.CDPHello.SubVersion = v[14]
|
||||
info.CDPHello.Status = v[15]
|
||||
info.CDPHello.Unknown2 = v[16]
|
||||
info.CDPHello.ClusterCommander = v[17:23]
|
||||
info.CDPHello.SwitchMAC = v[23:29]
|
||||
info.CDPHello.Unknown3 = v[29]
|
||||
info.CDPHello.ManagementVLAN = binary.BigEndian.Uint16(v[30:32])
|
||||
case CDPTLVVTPDomain:
|
||||
info.VTPDomain = string(val.Value)
|
||||
case CDPTLVNativeVLAN:
|
||||
if err = checkCDPTLVLen(val, 2); err != nil {
|
||||
return err
|
||||
}
|
||||
info.NativeVLAN = binary.BigEndian.Uint16(val.Value[0:2])
|
||||
case CDPTLVFullDuplex:
|
||||
if err = checkCDPTLVLen(val, 1); err != nil {
|
||||
return err
|
||||
}
|
||||
info.FullDuplex = (val.Value[0] == 1)
|
||||
case CDPTLVVLANReply:
|
||||
if err = checkCDPTLVLen(val, 3); err != nil {
|
||||
return err
|
||||
}
|
||||
info.VLANReply.ID = uint8(val.Value[0])
|
||||
info.VLANReply.VLAN = binary.BigEndian.Uint16(val.Value[1:3])
|
||||
case CDPTLVVLANQuery:
|
||||
if err = checkCDPTLVLen(val, 3); err != nil {
|
||||
return err
|
||||
}
|
||||
info.VLANQuery.ID = uint8(val.Value[0])
|
||||
info.VLANQuery.VLAN = binary.BigEndian.Uint16(val.Value[1:3])
|
||||
case CDPTLVPower:
|
||||
if err = checkCDPTLVLen(val, 2); err != nil {
|
||||
return err
|
||||
}
|
||||
info.PowerConsumption = binary.BigEndian.Uint16(val.Value[0:2])
|
||||
case CDPTLVMTU:
|
||||
if err = checkCDPTLVLen(val, 4); err != nil {
|
||||
return err
|
||||
}
|
||||
info.MTU = binary.BigEndian.Uint32(val.Value[0:4])
|
||||
case CDPTLVExtendedTrust:
|
||||
if err = checkCDPTLVLen(val, 1); err != nil {
|
||||
return err
|
||||
}
|
||||
info.ExtendedTrust = uint8(val.Value[0])
|
||||
case CDPTLVUntrustedCOS:
|
||||
if err = checkCDPTLVLen(val, 1); err != nil {
|
||||
return err
|
||||
}
|
||||
info.UntrustedCOS = uint8(val.Value[0])
|
||||
case CDPTLVSysName:
|
||||
info.SysName = string(val.Value)
|
||||
case CDPTLVSysOID:
|
||||
info.SysOID = string(val.Value)
|
||||
case CDPTLVMgmtAddresses:
|
||||
if err = checkCDPTLVLen(val, 4); err != nil {
|
||||
return err
|
||||
}
|
||||
info.MgmtAddresses, err = decodeAddresses(val.Value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
case CDPTLVLocation:
|
||||
if err = checkCDPTLVLen(val, 2); err != nil {
|
||||
return err
|
||||
}
|
||||
info.Location.Type = uint8(val.Value[0])
|
||||
info.Location.Location = string(val.Value[1:])
|
||||
|
||||
// case CDPTLVLExternalPortID:
|
||||
// Undocumented
|
||||
case CDPTLVPowerRequested:
|
||||
if err = checkCDPTLVLen(val, 4); err != nil {
|
||||
return err
|
||||
}
|
||||
info.PowerRequest.ID = binary.BigEndian.Uint16(val.Value[0:2])
|
||||
info.PowerRequest.MgmtID = binary.BigEndian.Uint16(val.Value[2:4])
|
||||
for n := 4; n < len(val.Value); n += 4 {
|
||||
info.PowerRequest.Values = append(info.PowerRequest.Values, binary.BigEndian.Uint32(val.Value[n:n+4]))
|
||||
}
|
||||
case CDPTLVPowerAvailable:
|
||||
if err = checkCDPTLVLen(val, 4); err != nil {
|
||||
return err
|
||||
}
|
||||
info.PowerAvailable.ID = binary.BigEndian.Uint16(val.Value[0:2])
|
||||
info.PowerAvailable.MgmtID = binary.BigEndian.Uint16(val.Value[2:4])
|
||||
for n := 4; n < len(val.Value); n += 4 {
|
||||
info.PowerAvailable.Values = append(info.PowerAvailable.Values, binary.BigEndian.Uint32(val.Value[n:n+4]))
|
||||
}
|
||||
// case CDPTLVPortUnidirectional
|
||||
// Undocumented
|
||||
case CDPTLVEnergyWise:
|
||||
if err = checkCDPTLVLen(val, 72); err != nil {
|
||||
return err
|
||||
}
|
||||
info.EnergyWise.EncryptedData = val.Value[0:20]
|
||||
info.EnergyWise.Unknown1 = binary.BigEndian.Uint32(val.Value[20:24])
|
||||
info.EnergyWise.SequenceNumber = binary.BigEndian.Uint32(val.Value[24:28])
|
||||
info.EnergyWise.ModelNumber = string(val.Value[28:44])
|
||||
info.EnergyWise.Unknown2 = binary.BigEndian.Uint16(val.Value[44:46])
|
||||
info.EnergyWise.HardwareID = string(val.Value[46:49])
|
||||
info.EnergyWise.SerialNum = string(val.Value[49:60])
|
||||
info.EnergyWise.Unknown3 = val.Value[60:68]
|
||||
tlvLen := binary.BigEndian.Uint16(val.Value[68:70])
|
||||
tlvNum := binary.BigEndian.Uint16(val.Value[70:72])
|
||||
data := val.Value[72:]
|
||||
if len(data) < int(tlvLen) {
|
||||
return fmt.Errorf("Invalid TLV length %d vs %d", tlvLen, len(data))
|
||||
}
|
||||
numSeen := 0
|
||||
for len(data) > 8 {
|
||||
numSeen++
|
||||
if numSeen > int(tlvNum) { // Too many TLV's ?
|
||||
return fmt.Errorf("Too many TLV's - wanted %d, saw %d", tlvNum, numSeen)
|
||||
}
|
||||
tType := CDPEnergyWiseSubtype(binary.BigEndian.Uint32(data[0:4]))
|
||||
tLen := int(binary.BigEndian.Uint32(data[4:8]))
|
||||
if tLen > len(data)-8 {
|
||||
return fmt.Errorf("Invalid TLV length %d vs %d", tLen, len(data)-8)
|
||||
}
|
||||
data = data[8:]
|
||||
switch tType {
|
||||
case CDPEnergyWiseRole:
|
||||
info.EnergyWise.Role = string(data[:])
|
||||
case CDPEnergyWiseDomain:
|
||||
info.EnergyWise.Domain = string(data[:])
|
||||
case CDPEnergyWiseName:
|
||||
info.EnergyWise.Name = string(data[:])
|
||||
case CDPEnergyWiseReplyTo:
|
||||
if len(data) >= 18 {
|
||||
info.EnergyWise.ReplyUnknown1 = data[0:2]
|
||||
info.EnergyWise.ReplyPort = data[2:4]
|
||||
info.EnergyWise.ReplyAddress = data[4:8]
|
||||
info.EnergyWise.ReplyUnknown2 = data[8:10]
|
||||
info.EnergyWise.ReplyUnknown3 = data[10:14]
|
||||
}
|
||||
}
|
||||
data = data[tLen:]
|
||||
}
|
||||
case CDPTLVSparePairPOE:
|
||||
if err = checkCDPTLVLen(val, 1); err != nil {
|
||||
return err
|
||||
}
|
||||
v := val.Value[0]
|
||||
info.SparePairPoe.PSEFourWire = (v&CDPPoEFourWire > 0)
|
||||
info.SparePairPoe.PDArchShared = (v&CDPPoEPDArch > 0)
|
||||
info.SparePairPoe.PDRequestOn = (v&CDPPoEPDRequest > 0)
|
||||
info.SparePairPoe.PSEOn = (v&CDPPoEPSE > 0)
|
||||
default:
|
||||
info.Unknown = append(info.Unknown, val)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// CDP Protocol Types
|
||||
const (
|
||||
CDPProtocolTypeNLPID byte = 1
|
||||
CDPProtocolType802_2 byte = 2
|
||||
)
|
||||
|
||||
// CDPAddressType is used to define TLV values within CDP addresses.
|
||||
type CDPAddressType uint64
|
||||
|
||||
// CDP Address types.
|
||||
const (
|
||||
CDPAddressTypeCLNP CDPAddressType = 0x81
|
||||
CDPAddressTypeIPV4 CDPAddressType = 0xcc
|
||||
CDPAddressTypeIPV6 CDPAddressType = 0xaaaa030000000800
|
||||
CDPAddressTypeDECNET CDPAddressType = 0xaaaa030000006003
|
||||
CDPAddressTypeAPPLETALK CDPAddressType = 0xaaaa03000000809b
|
||||
CDPAddressTypeIPX CDPAddressType = 0xaaaa030000008137
|
||||
CDPAddressTypeVINES CDPAddressType = 0xaaaa0300000080c4
|
||||
CDPAddressTypeXNS CDPAddressType = 0xaaaa030000000600
|
||||
CDPAddressTypeAPOLLO CDPAddressType = 0xaaaa030000008019
|
||||
)
|
||||
|
||||
func decodeAddresses(v []byte) (addresses []net.IP, err error) {
|
||||
numaddr := int(binary.BigEndian.Uint32(v[0:4]))
|
||||
if numaddr < 1 {
|
||||
return nil, fmt.Errorf("Invalid Address TLV number %d", numaddr)
|
||||
}
|
||||
v = v[4:]
|
||||
if len(v) < numaddr*8 {
|
||||
return nil, fmt.Errorf("Invalid Address TLV length %d", len(v))
|
||||
}
|
||||
for i := 0; i < numaddr; i++ {
|
||||
prottype := v[0]
|
||||
if prottype != CDPProtocolTypeNLPID && prottype != CDPProtocolType802_2 { // invalid protocol type
|
||||
return nil, fmt.Errorf("Invalid Address Protocol %d", prottype)
|
||||
}
|
||||
protlen := int(v[1])
|
||||
if (prottype == CDPProtocolTypeNLPID && protlen != 1) ||
|
||||
(prottype == CDPProtocolType802_2 && protlen != 3 && protlen != 8) { // invalid length
|
||||
return nil, fmt.Errorf("Invalid Address Protocol length %d", protlen)
|
||||
}
|
||||
plen := make([]byte, 8)
|
||||
copy(plen[8-protlen:], v[2:2+protlen])
|
||||
protocol := CDPAddressType(binary.BigEndian.Uint64(plen))
|
||||
v = v[2+protlen:]
|
||||
addrlen := binary.BigEndian.Uint16(v[0:2])
|
||||
ab := v[2 : 2+addrlen]
|
||||
if protocol == CDPAddressTypeIPV4 && addrlen == 4 {
|
||||
addresses = append(addresses, net.IPv4(ab[0], ab[1], ab[2], ab[3]))
|
||||
} else if protocol == CDPAddressTypeIPV6 && addrlen == 16 {
|
||||
addresses = append(addresses, net.IP(ab))
|
||||
} else {
|
||||
// only handle IPV4 & IPV6 for now
|
||||
}
|
||||
v = v[2+addrlen:]
|
||||
if len(v) < 8 {
|
||||
break
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (t CDPTLVType) String() (s string) {
|
||||
switch t {
|
||||
case CDPTLVDevID:
|
||||
s = "Device ID"
|
||||
case CDPTLVAddress:
|
||||
s = "Addresses"
|
||||
case CDPTLVPortID:
|
||||
s = "Port ID"
|
||||
case CDPTLVCapabilities:
|
||||
s = "Capabilities"
|
||||
case CDPTLVVersion:
|
||||
s = "Software Version"
|
||||
case CDPTLVPlatform:
|
||||
s = "Platform"
|
||||
case CDPTLVIPPrefix:
|
||||
s = "IP Prefix"
|
||||
case CDPTLVHello:
|
||||
s = "Protocol Hello"
|
||||
case CDPTLVVTPDomain:
|
||||
s = "VTP Management Domain"
|
||||
case CDPTLVNativeVLAN:
|
||||
s = "Native VLAN"
|
||||
case CDPTLVFullDuplex:
|
||||
s = "Full Duplex"
|
||||
case CDPTLVVLANReply:
|
||||
s = "VoIP VLAN Reply"
|
||||
case CDPTLVVLANQuery:
|
||||
s = "VLANQuery"
|
||||
case CDPTLVPower:
|
||||
s = "Power consumption"
|
||||
case CDPTLVMTU:
|
||||
s = "MTU"
|
||||
case CDPTLVExtendedTrust:
|
||||
s = "Extended Trust Bitmap"
|
||||
case CDPTLVUntrustedCOS:
|
||||
s = "Untrusted Port CoS"
|
||||
case CDPTLVSysName:
|
||||
s = "System Name"
|
||||
case CDPTLVSysOID:
|
||||
s = "System OID"
|
||||
case CDPTLVMgmtAddresses:
|
||||
s = "Management Addresses"
|
||||
case CDPTLVLocation:
|
||||
s = "Location"
|
||||
case CDPTLVExternalPortID:
|
||||
s = "External Port ID"
|
||||
case CDPTLVPowerRequested:
|
||||
s = "Power Requested"
|
||||
case CDPTLVPowerAvailable:
|
||||
s = "Power Available"
|
||||
case CDPTLVPortUnidirectional:
|
||||
s = "Port Unidirectional"
|
||||
case CDPTLVEnergyWise:
|
||||
s = "Energy Wise"
|
||||
case CDPTLVSparePairPOE:
|
||||
s = "Spare Pair POE"
|
||||
default:
|
||||
s = "Unknown"
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (a CDPAddressType) String() (s string) {
|
||||
switch a {
|
||||
case CDPAddressTypeCLNP:
|
||||
s = "Connectionless Network Protocol"
|
||||
case CDPAddressTypeIPV4:
|
||||
s = "IPv4"
|
||||
case CDPAddressTypeIPV6:
|
||||
s = "IPv6"
|
||||
case CDPAddressTypeDECNET:
|
||||
s = "DECnet Phase IV"
|
||||
case CDPAddressTypeAPPLETALK:
|
||||
s = "Apple Talk"
|
||||
case CDPAddressTypeIPX:
|
||||
s = "Novell IPX"
|
||||
case CDPAddressTypeVINES:
|
||||
s = "Banyan VINES"
|
||||
case CDPAddressTypeXNS:
|
||||
s = "Xerox Network Systems"
|
||||
case CDPAddressTypeAPOLLO:
|
||||
s = "Apollo"
|
||||
default:
|
||||
s = "Unknown"
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (t CDPEnergyWiseSubtype) String() (s string) {
|
||||
switch t {
|
||||
case CDPEnergyWiseRole:
|
||||
s = "Role"
|
||||
case CDPEnergyWiseDomain:
|
||||
s = "Domain"
|
||||
case CDPEnergyWiseName:
|
||||
s = "Name"
|
||||
case CDPEnergyWiseReplyTo:
|
||||
s = "ReplyTo"
|
||||
default:
|
||||
s = "Unknown"
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func checkCDPTLVLen(v CiscoDiscoveryValue, l int) (err error) {
|
||||
if len(v.Value) < l {
|
||||
err = fmt.Errorf("Invalid TLV %v length %d", v.Type, len(v.Value))
|
||||
}
|
||||
return
|
||||
}
|
||||
+109
@@ -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)
|
||||
}
|
||||
+585
@@ -0,0 +1,585 @@
|
||||
// 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 {
|
||||
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
|
||||
}
|
||||
}
|
||||
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")
|
||||
)
|
||||
+341
@@ -0,0 +1,341 @@
|
||||
// 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"
|
||||
"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 {
|
||||
d.BaseLayer = BaseLayer{Contents: data}
|
||||
d.Options = d.Options[:0]
|
||||
d.MsgType = DHCPv6MsgType(data[0])
|
||||
|
||||
offset := 0
|
||||
if d.MsgType == DHCPv6MsgTypeRelayForward || d.MsgType == DHCPv6MsgTypeRelayReply {
|
||||
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 errors.New("Not enough bytes to decode: " + string(len(data)))
|
||||
}
|
||||
|
||||
d.Type = DHCPv6DUIDType(binary.BigEndian.Uint16(data[:2]))
|
||||
if d.Type == DHCPv6DUIDTypeLLT || d.Type == DHCPv6DUIDTypeLL {
|
||||
d.HardwareType = data[2:4]
|
||||
}
|
||||
|
||||
if d.Type == DHCPv6DUIDTypeLLT {
|
||||
d.Time = data[4:8]
|
||||
d.LinkLayerAddress = net.HardwareAddr(data[8:])
|
||||
} else if d.Type == DHCPv6DUIDTypeEN {
|
||||
d.EnterpriseNumber = data[2:6]
|
||||
d.Identifier = data[6:]
|
||||
} else { // DHCPv6DUIDTypeLL
|
||||
d.LinkLayerAddress = net.HardwareAddr(data[4:])
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Encode encodes the DHCPv6DUID in a slice of bytes
|
||||
func (d *DHCPv6DUID) Encode() []byte {
|
||||
length := d.Len()
|
||||
data := make([]byte, length)
|
||||
binary.BigEndian.PutUint16(data[0:2], uint16(d.Type))
|
||||
|
||||
if d.Type == DHCPv6DUIDTypeLLT || d.Type == DHCPv6DUIDTypeLL {
|
||||
copy(data[2:4], d.HardwareType)
|
||||
}
|
||||
|
||||
if d.Type == DHCPv6DUIDTypeLLT {
|
||||
copy(data[4:8], d.Time)
|
||||
copy(data[8:], d.LinkLayerAddress)
|
||||
} else if d.Type == DHCPv6DUIDTypeEN {
|
||||
copy(data[2:6], d.EnterpriseNumber)
|
||||
copy(data[6:], d.Identifier)
|
||||
} else {
|
||||
copy(data[4:], d.LinkLayerAddress)
|
||||
}
|
||||
|
||||
return data
|
||||
}
|
||||
|
||||
// Len returns the length of the DHCPv6DUID, respecting the type
|
||||
func (d *DHCPv6DUID) Len() int {
|
||||
length := 2 // d.Type
|
||||
if d.Type == DHCPv6DUIDTypeLLT {
|
||||
length += 2 /*HardwareType*/ + 4 /*d.Time*/ + len(d.LinkLayerAddress)
|
||||
} else if d.Type == DHCPv6DUIDTypeEN {
|
||||
length += 4 /*d.EnterpriseNumber*/ + len(d.Identifier)
|
||||
} else { // LL
|
||||
length += 2 /*d.HardwareType*/ + len(d.LinkLayerAddress)
|
||||
}
|
||||
|
||||
return length
|
||||
}
|
||||
|
||||
func (d *DHCPv6DUID) String() string {
|
||||
duid := "Type: " + d.Type.String() + ", "
|
||||
if d.Type == DHCPv6DUIDTypeLLT {
|
||||
duid += fmt.Sprintf("HardwareType: %v, Time: %v, LinkLayerAddress: %v", d.HardwareType, d.Time, d.LinkLayerAddress)
|
||||
} else if d.Type == DHCPv6DUIDTypeEN {
|
||||
duid += fmt.Sprintf("EnterpriseNumber: %v, Identifier: %v", d.EnterpriseNumber, d.Identifier)
|
||||
} else { // DHCPv6DUIDTypeLL
|
||||
duid += fmt.Sprintf("HardwareType: %v, LinkLayerAddress: %v", d.HardwareType, d.LinkLayerAddress)
|
||||
}
|
||||
return duid
|
||||
}
|
||||
|
||||
func decodeDHCPv6DUID(data []byte) (*DHCPv6DUID, error) {
|
||||
duid := &DHCPv6DUID{}
|
||||
err := duid.DecodeFromBytes(data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return duid, nil
|
||||
}
|
||||
+621
@@ -0,0 +1,621 @@
|
||||
// Copyright 2018 The GoPacket Authors. All rights reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the LICENSE file in the root of the source
|
||||
// tree.
|
||||
|
||||
package layers
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/google/gopacket"
|
||||
)
|
||||
|
||||
// DHCPv6Opt represents a DHCP option or parameter from RFC-3315
|
||||
type DHCPv6Opt uint16
|
||||
|
||||
// Constants for the DHCPv6Opt options.
|
||||
const (
|
||||
DHCPv6OptClientID DHCPv6Opt = 1
|
||||
DHCPv6OptServerID DHCPv6Opt = 2
|
||||
DHCPv6OptIANA DHCPv6Opt = 3
|
||||
DHCPv6OptIATA DHCPv6Opt = 4
|
||||
DHCPv6OptIAAddr DHCPv6Opt = 5
|
||||
DHCPv6OptOro DHCPv6Opt = 6
|
||||
DHCPv6OptPreference DHCPv6Opt = 7
|
||||
DHCPv6OptElapsedTime DHCPv6Opt = 8
|
||||
DHCPv6OptRelayMessage DHCPv6Opt = 9
|
||||
DHCPv6OptAuth DHCPv6Opt = 11
|
||||
DHCPv6OptUnicast DHCPv6Opt = 12
|
||||
DHCPv6OptStatusCode DHCPv6Opt = 13
|
||||
DHCPv6OptRapidCommit DHCPv6Opt = 14
|
||||
DHCPv6OptUserClass DHCPv6Opt = 15
|
||||
DHCPv6OptVendorClass DHCPv6Opt = 16
|
||||
DHCPv6OptVendorOpts DHCPv6Opt = 17
|
||||
DHCPv6OptInterfaceID DHCPv6Opt = 18
|
||||
DHCPv6OptReconfigureMessage DHCPv6Opt = 19
|
||||
DHCPv6OptReconfigureAccept DHCPv6Opt = 20
|
||||
|
||||
// RFC 3319 Session Initiation Protocol (SIP)
|
||||
DHCPv6OptSIPServersDomainList DHCPv6Opt = 21
|
||||
DHCPv6OptSIPServersAddressList DHCPv6Opt = 22
|
||||
|
||||
// RFC 3646 DNS Configuration
|
||||
DHCPv6OptDNSServers DHCPv6Opt = 23
|
||||
DHCPv6OptDomainList DHCPv6Opt = 24
|
||||
|
||||
// RFC 3633 Prefix Delegation
|
||||
DHCPv6OptIAPD DHCPv6Opt = 25
|
||||
DHCPv6OptIAPrefix DHCPv6Opt = 26
|
||||
|
||||
// RFC 3898 Network Information Service (NIS)
|
||||
DHCPv6OptNISServers DHCPv6Opt = 27
|
||||
DHCPv6OptNISPServers DHCPv6Opt = 28
|
||||
DHCPv6OptNISDomainName DHCPv6Opt = 29
|
||||
DHCPv6OptNISPDomainName DHCPv6Opt = 30
|
||||
|
||||
// RFC 4075 Simple Network Time Protocol (SNTP)
|
||||
DHCPv6OptSNTPServers DHCPv6Opt = 31
|
||||
|
||||
// RFC 4242 Information Refresh Time Option
|
||||
DHCPv6OptInformationRefreshTime DHCPv6Opt = 32
|
||||
|
||||
// RFC 4280 Broadcast and Multicast Control Servers
|
||||
DHCPv6OptBCMCSServerDomainNameList DHCPv6Opt = 33
|
||||
DHCPv6OptBCMCSServerAddressList DHCPv6Opt = 34
|
||||
|
||||
// RFC 4776 Civic Address ConfigurationOption
|
||||
DHCPv6OptGeoconfCivic DHCPv6Opt = 36
|
||||
|
||||
// RFC 4649 Relay Agent Remote-ID
|
||||
DHCPv6OptRemoteID DHCPv6Opt = 37
|
||||
|
||||
// RFC 4580 Relay Agent Subscriber-ID
|
||||
DHCPv6OptSubscriberID DHCPv6Opt = 38
|
||||
|
||||
// RFC 4704 Client Full Qualified Domain Name (FQDN)
|
||||
DHCPv6OptClientFQDN DHCPv6Opt = 39
|
||||
|
||||
// RFC 5192 Protocol for Carrying Authentication for Network Access (PANA)
|
||||
DHCPv6OptPanaAgent DHCPv6Opt = 40
|
||||
|
||||
// RFC 4833 Timezone Options
|
||||
DHCPv6OptNewPOSIXTimezone DHCPv6Opt = 41
|
||||
DHCPv6OptNewTZDBTimezone DHCPv6Opt = 42
|
||||
|
||||
// RFC 4994 Relay Agent Echo Request
|
||||
DHCPv6OptEchoRequestOption DHCPv6Opt = 43
|
||||
|
||||
// RFC 5007 Leasequery
|
||||
DHCPv6OptLQQuery DHCPv6Opt = 44
|
||||
DHCPv6OptCLTTime DHCPv6Opt = 45
|
||||
DHCPv6OptClientData DHCPv6Opt = 46
|
||||
DHCPv6OptLQRelayData DHCPv6Opt = 47
|
||||
DHCPv6OptLQClientLink DHCPv6Opt = 48
|
||||
|
||||
// RFC 6610 Home Information Discovery in Mobile IPv6 (MIPv6)
|
||||
DHCPv6OptMIP6HNIDF DHCPv6Opt = 49
|
||||
DHCPv6OptMIP6VDINF DHCPv6Opt = 50
|
||||
DHCPv6OptMIP6IDINF DHCPv6Opt = 69
|
||||
DHCPv6OptMIP6UDINF DHCPv6Opt = 70
|
||||
DHCPv6OptMIP6HNP DHCPv6Opt = 71
|
||||
DHCPv6OptMIP6HAA DHCPv6Opt = 72
|
||||
DHCPv6OptMIP6HAF DHCPv6Opt = 73
|
||||
|
||||
// RFC 5223 Discovering Location-to-Service Translation (LoST) Servers
|
||||
DHCPv6OptV6LOST DHCPv6Opt = 51
|
||||
|
||||
// RFC 5417 Control And Provisioning of Wireless Access Points (CAPWAP)
|
||||
DHCPv6OptCAPWAPACV6 DHCPv6Opt = 52
|
||||
|
||||
// RFC 5460 Bulk Leasequery
|
||||
DHCPv6OptRelayID DHCPv6Opt = 53
|
||||
|
||||
// RFC 5678 IEEE 802.21 Mobility Services (MoS) Discovery
|
||||
DHCPv6OptIPv6AddressMoS DHCPv6Opt = 54
|
||||
DHCPv6OptIPv6FQDNMoS DHCPv6Opt = 55
|
||||
|
||||
// RFC 5908 NTP Server Option
|
||||
DHCPv6OptNTPServer DHCPv6Opt = 56
|
||||
|
||||
// RFC 5986 Discovering the Local Location Information Server (LIS)
|
||||
DHCPv6OptV6AccessDomain DHCPv6Opt = 57
|
||||
|
||||
// RFC 5986 SIP User Agent
|
||||
DHCPv6OptSIPUACSList DHCPv6Opt = 58
|
||||
|
||||
// RFC 5970 Options for Network Boot
|
||||
DHCPv6OptBootFileURL DHCPv6Opt = 59
|
||||
DHCPv6OptBootFileParam DHCPv6Opt = 60
|
||||
DHCPv6OptClientArchType DHCPv6Opt = 61
|
||||
DHCPv6OptNII DHCPv6Opt = 62
|
||||
|
||||
// RFC 6225 Coordinate-Based Location Configuration Information
|
||||
DHCPv6OptGeolocation DHCPv6Opt = 63
|
||||
|
||||
// RFC 6334 Dual-Stack Lite
|
||||
DHCPv6OptAFTRName DHCPv6Opt = 64
|
||||
|
||||
// RFC 6440 EAP Re-authentication Protocol (ERP)
|
||||
DHCPv6OptERPLocalDomainName DHCPv6Opt = 65
|
||||
|
||||
// RFC 6422 Relay-Supplied DHCP Options
|
||||
DHCPv6OptRSOO DHCPv6Opt = 66
|
||||
|
||||
// RFC 6603 Prefix Exclude Option for DHCPv6-based Prefix Delegation
|
||||
DHCPv6OptPDExclude DHCPv6Opt = 67
|
||||
|
||||
// RFC 6607 Virtual Subnet Selection
|
||||
DHCPv6OptVSS DHCPv6Opt = 68
|
||||
|
||||
// RFC 6731 Improved Recursive DNS Server Selection for Multi-Interfaced Nodes
|
||||
DHCPv6OptRDNSSSelection DHCPv6Opt = 74
|
||||
|
||||
// RFC 6784 Kerberos Options for DHCPv6
|
||||
DHCPv6OptKRBPrincipalName DHCPv6Opt = 75
|
||||
DHCPv6OptKRBRealmName DHCPv6Opt = 76
|
||||
DHCPv6OptKRBKDC DHCPv6Opt = 77
|
||||
|
||||
// RFC 6939 Client Link-Layer Address Option
|
||||
DHCPv6OptClientLinkLayerAddress DHCPv6Opt = 79
|
||||
|
||||
// RFC 6977 Triggering DHCPv6 Reconfiguration from Relay Agents
|
||||
DHCPv6OptLinkAddress DHCPv6Opt = 80
|
||||
|
||||
// RFC 7037 RADIUS Option for the DHCPv6 Relay Agent
|
||||
DHCPv6OptRADIUS DHCPv6Opt = 81
|
||||
|
||||
// RFC 7083 Modification to Default Values of SOL_MAX_RT and INF_MAX_RT
|
||||
DHCPv6OptSolMaxRt DHCPv6Opt = 82
|
||||
DHCPv6OptInfMaxRt DHCPv6Opt = 83
|
||||
|
||||
// RFC 7078 Distributing Address Selection Policy
|
||||
DHCPv6OptAddrSel DHCPv6Opt = 84
|
||||
DHCPv6OptAddrSelTable DHCPv6Opt = 85
|
||||
|
||||
// RFC 7291 DHCP Options for the Port Control Protocol (PCP)
|
||||
DHCPv6OptV6PCPServer DHCPv6Opt = 86
|
||||
|
||||
// RFC 7341 DHCPv4-over-DHCPv6 (DHCP 4o6) Transport
|
||||
DHCPv6OptDHCPv4Message DHCPv6Opt = 87
|
||||
DHCPv6OptDHCPv4OverDHCPv6Server DHCPv6Opt = 88
|
||||
|
||||
// RFC 7598 Configuration of Softwire Address and Port-Mapped Clients
|
||||
DHCPv6OptS46Rule DHCPv6Opt = 89
|
||||
DHCPv6OptS46BR DHCPv6Opt = 90
|
||||
DHCPv6OptS46DMR DHCPv6Opt = 91
|
||||
DHCPv6OptS46V4V4Bind DHCPv6Opt = 92
|
||||
DHCPv6OptS46PortParameters DHCPv6Opt = 93
|
||||
DHCPv6OptS46ContMAPE DHCPv6Opt = 94
|
||||
DHCPv6OptS46ContMAPT DHCPv6Opt = 95
|
||||
DHCPv6OptS46ContLW DHCPv6Opt = 96
|
||||
|
||||
// RFC 7600 IPv4 Residual Deployment via IPv6
|
||||
DHCPv6Opt4RD DHCPv6Opt = 97
|
||||
DHCPv6Opt4RDMapRule DHCPv6Opt = 98
|
||||
DHCPv6Opt4RDNonMapRule DHCPv6Opt = 99
|
||||
|
||||
// RFC 7653 Active Leasequery
|
||||
DHCPv6OptLQBaseTime DHCPv6Opt = 100
|
||||
DHCPv6OptLQStartTime DHCPv6Opt = 101
|
||||
DHCPv6OptLQEndTime DHCPv6Opt = 102
|
||||
|
||||
// RFC 7710 Captive-Portal Identification
|
||||
DHCPv6OptCaptivePortal DHCPv6Opt = 103
|
||||
|
||||
// RFC 7774 Multicast Protocol for Low-Power and Lossy Networks (MPL) Parameter Configuration
|
||||
DHCPv6OptMPLParameters DHCPv6Opt = 104
|
||||
|
||||
// RFC 7839 Access-Network-Identifier (ANI)
|
||||
DHCPv6OptANIATT DHCPv6Opt = 105
|
||||
DHCPv6OptANINetworkName DHCPv6Opt = 106
|
||||
DHCPv6OptANIAPName DHCPv6Opt = 107
|
||||
DHCPv6OptANIAPBSSID DHCPv6Opt = 108
|
||||
DHCPv6OptANIOperatorID DHCPv6Opt = 109
|
||||
DHCPv6OptANIOperatorRealm DHCPv6Opt = 110
|
||||
|
||||
// RFC 8026 Unified IPv4-in-IPv6 Softwire Customer Premises Equipment (CPE)
|
||||
DHCPv6OptS46Priority DHCPv6Opt = 111
|
||||
|
||||
// draft-ietf-opsawg-mud-25 Manufacturer Usage Description (MUD)
|
||||
DHCPv6OptMUDURLV6 DHCPv6Opt = 112
|
||||
|
||||
// RFC 8115 IPv4-Embedded Multicast and Unicast IPv6 Prefixes
|
||||
DHCPv6OptV6Prefix64 DHCPv6Opt = 113
|
||||
|
||||
// RFC 8156 DHCPv6 Failover Protocol
|
||||
DHCPv6OptFBindingStatus DHCPv6Opt = 114
|
||||
DHCPv6OptFConnectFlags DHCPv6Opt = 115
|
||||
DHCPv6OptFDNSRemovalInfo DHCPv6Opt = 116
|
||||
DHCPv6OptFDNSHostName DHCPv6Opt = 117
|
||||
DHCPv6OptFDNSZoneName DHCPv6Opt = 118
|
||||
DHCPv6OptFDNSFlags DHCPv6Opt = 119
|
||||
DHCPv6OptFExpirationTime DHCPv6Opt = 120
|
||||
DHCPv6OptFMaxUnacknowledgedBNDUPD DHCPv6Opt = 121
|
||||
DHCPv6OptFMCLT DHCPv6Opt = 122
|
||||
DHCPv6OptFPartnerLifetime DHCPv6Opt = 123
|
||||
DHCPv6OptFPartnerLifetimeSent DHCPv6Opt = 124
|
||||
DHCPv6OptFPartnerDownTime DHCPv6Opt = 125
|
||||
DHCPv6OptFPartnerRawCltTime DHCPv6Opt = 126
|
||||
DHCPv6OptFProtocolVersion DHCPv6Opt = 127
|
||||
DHCPv6OptFKeepaliveTime DHCPv6Opt = 128
|
||||
DHCPv6OptFReconfigureData DHCPv6Opt = 129
|
||||
DHCPv6OptFRelationshipName DHCPv6Opt = 130
|
||||
DHCPv6OptFServerFlags DHCPv6Opt = 131
|
||||
DHCPv6OptFServerState DHCPv6Opt = 132
|
||||
DHCPv6OptFStartTimeOfState DHCPv6Opt = 133
|
||||
DHCPv6OptFStateExpirationTime DHCPv6Opt = 134
|
||||
|
||||
// RFC 8357 Generalized UDP Source Port for DHCP Relay
|
||||
DHCPv6OptRelayPort DHCPv6Opt = 135
|
||||
|
||||
// draft-ietf-netconf-zerotouch-25 Zero Touch Provisioning for Networking Devices
|
||||
DHCPv6OptV6ZeroTouchRedirect DHCPv6Opt = 136
|
||||
|
||||
// RFC 6153 Access Network Discovery and Selection Function (ANDSF) Discovery
|
||||
DHCPv6OptIPV6AddressANDSF DHCPv6Opt = 143
|
||||
)
|
||||
|
||||
// String returns a string version of a DHCPv6Opt.
|
||||
func (o DHCPv6Opt) String() string {
|
||||
switch o {
|
||||
case DHCPv6OptClientID:
|
||||
return "ClientID"
|
||||
case DHCPv6OptServerID:
|
||||
return "ServerID"
|
||||
case DHCPv6OptIANA:
|
||||
return "IA_NA"
|
||||
case DHCPv6OptIATA:
|
||||
return "IA_TA"
|
||||
case DHCPv6OptIAAddr:
|
||||
return "IAAddr"
|
||||
case DHCPv6OptOro:
|
||||
return "Oro"
|
||||
case DHCPv6OptPreference:
|
||||
return "Preference"
|
||||
case DHCPv6OptElapsedTime:
|
||||
return "ElapsedTime"
|
||||
case DHCPv6OptRelayMessage:
|
||||
return "RelayMessage"
|
||||
case DHCPv6OptAuth:
|
||||
return "Auth"
|
||||
case DHCPv6OptUnicast:
|
||||
return "Unicast"
|
||||
case DHCPv6OptStatusCode:
|
||||
return "StatusCode"
|
||||
case DHCPv6OptRapidCommit:
|
||||
return "RapidCommit"
|
||||
case DHCPv6OptUserClass:
|
||||
return "UserClass"
|
||||
case DHCPv6OptVendorClass:
|
||||
return "VendorClass"
|
||||
case DHCPv6OptVendorOpts:
|
||||
return "VendorOpts"
|
||||
case DHCPv6OptInterfaceID:
|
||||
return "InterfaceID"
|
||||
case DHCPv6OptReconfigureMessage:
|
||||
return "ReconfigureMessage"
|
||||
case DHCPv6OptReconfigureAccept:
|
||||
return "ReconfigureAccept"
|
||||
case DHCPv6OptSIPServersDomainList:
|
||||
return "SIPServersDomainList"
|
||||
case DHCPv6OptSIPServersAddressList:
|
||||
return "SIPServersAddressList"
|
||||
case DHCPv6OptDNSServers:
|
||||
return "DNSRecursiveNameServer"
|
||||
case DHCPv6OptDomainList:
|
||||
return "DomainSearchList"
|
||||
case DHCPv6OptIAPD:
|
||||
return "IdentityAssociationPrefixDelegation"
|
||||
case DHCPv6OptIAPrefix:
|
||||
return "IAPDPrefix"
|
||||
case DHCPv6OptNISServers:
|
||||
return "NISServers"
|
||||
case DHCPv6OptNISPServers:
|
||||
return "NISv2Servers"
|
||||
case DHCPv6OptNISDomainName:
|
||||
return "NISDomainName"
|
||||
case DHCPv6OptNISPDomainName:
|
||||
return "NISv2DomainName"
|
||||
case DHCPv6OptSNTPServers:
|
||||
return "SNTPServers"
|
||||
case DHCPv6OptInformationRefreshTime:
|
||||
return "InformationRefreshTime"
|
||||
case DHCPv6OptBCMCSServerDomainNameList:
|
||||
return "BCMCSControlServersDomainNameList"
|
||||
case DHCPv6OptBCMCSServerAddressList:
|
||||
return "BCMCSControlServersAddressList"
|
||||
case DHCPv6OptGeoconfCivic:
|
||||
return "CivicAddress"
|
||||
case DHCPv6OptRemoteID:
|
||||
return "RelayAgentRemoteID"
|
||||
case DHCPv6OptSubscriberID:
|
||||
return "RelayAgentSubscriberID"
|
||||
case DHCPv6OptClientFQDN:
|
||||
return "ClientFQDN"
|
||||
case DHCPv6OptPanaAgent:
|
||||
return "PANAAuthenticationAgent"
|
||||
case DHCPv6OptNewPOSIXTimezone:
|
||||
return "NewPOSIXTimezone"
|
||||
case DHCPv6OptNewTZDBTimezone:
|
||||
return "NewTZDBTimezone"
|
||||
case DHCPv6OptEchoRequestOption:
|
||||
return "EchoRequest"
|
||||
case DHCPv6OptLQQuery:
|
||||
return "LeasequeryQuery"
|
||||
case DHCPv6OptClientData:
|
||||
return "LeasequeryClientData"
|
||||
case DHCPv6OptCLTTime:
|
||||
return "LeasequeryClientLastTransactionTime"
|
||||
case DHCPv6OptLQRelayData:
|
||||
return "LeasequeryRelayData"
|
||||
case DHCPv6OptLQClientLink:
|
||||
return "LeasequeryClientLink"
|
||||
case DHCPv6OptMIP6HNIDF:
|
||||
return "MIPv6HomeNetworkIDFQDN"
|
||||
case DHCPv6OptMIP6VDINF:
|
||||
return "MIPv6VisitedHomeNetworkInformation"
|
||||
case DHCPv6OptMIP6IDINF:
|
||||
return "MIPv6IdentifiedHomeNetworkInformation"
|
||||
case DHCPv6OptMIP6UDINF:
|
||||
return "MIPv6UnrestrictedHomeNetworkInformation"
|
||||
case DHCPv6OptMIP6HNP:
|
||||
return "MIPv6HomeNetworkPrefix"
|
||||
case DHCPv6OptMIP6HAA:
|
||||
return "MIPv6HomeAgentAddress"
|
||||
case DHCPv6OptMIP6HAF:
|
||||
return "MIPv6HomeAgentFQDN"
|
||||
case DHCPv6OptV6LOST:
|
||||
return "LoST Server"
|
||||
case DHCPv6OptCAPWAPACV6:
|
||||
return "CAPWAPAccessControllerV6"
|
||||
case DHCPv6OptRelayID:
|
||||
return "LeasequeryRelayID"
|
||||
case DHCPv6OptIPv6AddressMoS:
|
||||
return "MoSIPv6Address"
|
||||
case DHCPv6OptIPv6FQDNMoS:
|
||||
return "MoSDomainNameList"
|
||||
case DHCPv6OptNTPServer:
|
||||
return "NTPServer"
|
||||
case DHCPv6OptV6AccessDomain:
|
||||
return "AccessNetworkDomainName"
|
||||
case DHCPv6OptSIPUACSList:
|
||||
return "SIPUserAgentConfigurationServiceDomains"
|
||||
case DHCPv6OptBootFileURL:
|
||||
return "BootFileURL"
|
||||
case DHCPv6OptBootFileParam:
|
||||
return "BootFileParameters"
|
||||
case DHCPv6OptClientArchType:
|
||||
return "ClientSystemArchitectureType"
|
||||
case DHCPv6OptNII:
|
||||
return "ClientNetworkInterfaceIdentifier"
|
||||
case DHCPv6OptGeolocation:
|
||||
return "Geolocation"
|
||||
case DHCPv6OptAFTRName:
|
||||
return "AFTRName"
|
||||
case DHCPv6OptERPLocalDomainName:
|
||||
return "AFTRName"
|
||||
case DHCPv6OptRSOO:
|
||||
return "RSOOption"
|
||||
case DHCPv6OptPDExclude:
|
||||
return "PrefixExclude"
|
||||
case DHCPv6OptVSS:
|
||||
return "VirtualSubnetSelection"
|
||||
case DHCPv6OptRDNSSSelection:
|
||||
return "RDNSSSelection"
|
||||
case DHCPv6OptKRBPrincipalName:
|
||||
return "KerberosPrincipalName"
|
||||
case DHCPv6OptKRBRealmName:
|
||||
return "KerberosRealmName"
|
||||
case DHCPv6OptKRBKDC:
|
||||
return "KerberosKDC"
|
||||
case DHCPv6OptClientLinkLayerAddress:
|
||||
return "ClientLinkLayerAddress"
|
||||
case DHCPv6OptLinkAddress:
|
||||
return "LinkAddress"
|
||||
case DHCPv6OptRADIUS:
|
||||
return "RADIUS"
|
||||
case DHCPv6OptSolMaxRt:
|
||||
return "SolMaxRt"
|
||||
case DHCPv6OptInfMaxRt:
|
||||
return "InfMaxRt"
|
||||
case DHCPv6OptAddrSel:
|
||||
return "AddressSelection"
|
||||
case DHCPv6OptAddrSelTable:
|
||||
return "AddressSelectionTable"
|
||||
case DHCPv6OptV6PCPServer:
|
||||
return "PCPServer"
|
||||
case DHCPv6OptDHCPv4Message:
|
||||
return "DHCPv4Message"
|
||||
case DHCPv6OptDHCPv4OverDHCPv6Server:
|
||||
return "DHCP4o6ServerAddress"
|
||||
case DHCPv6OptS46Rule:
|
||||
return "S46Rule"
|
||||
case DHCPv6OptS46BR:
|
||||
return "S46BR"
|
||||
case DHCPv6OptS46DMR:
|
||||
return "S46DMR"
|
||||
case DHCPv6OptS46V4V4Bind:
|
||||
return "S46IPv4IPv6AddressBinding"
|
||||
case DHCPv6OptS46PortParameters:
|
||||
return "S46PortParameters"
|
||||
case DHCPv6OptS46ContMAPE:
|
||||
return "S46MAPEContainer"
|
||||
case DHCPv6OptS46ContMAPT:
|
||||
return "S46MAPTContainer"
|
||||
case DHCPv6OptS46ContLW:
|
||||
return "S46Lightweight4Over6Container"
|
||||
case DHCPv6Opt4RD:
|
||||
return "4RD"
|
||||
case DHCPv6Opt4RDMapRule:
|
||||
return "4RDMapRule"
|
||||
case DHCPv6Opt4RDNonMapRule:
|
||||
return "4RDNonMapRule"
|
||||
case DHCPv6OptLQBaseTime:
|
||||
return "LQBaseTime"
|
||||
case DHCPv6OptLQStartTime:
|
||||
return "LQStartTime"
|
||||
case DHCPv6OptLQEndTime:
|
||||
return "LQEndTime"
|
||||
case DHCPv6OptCaptivePortal:
|
||||
return "CaptivePortal"
|
||||
case DHCPv6OptMPLParameters:
|
||||
return "MPLParameterConfiguration"
|
||||
case DHCPv6OptANIATT:
|
||||
return "ANIAccessTechnologyType"
|
||||
case DHCPv6OptANINetworkName:
|
||||
return "ANINetworkName"
|
||||
case DHCPv6OptANIAPName:
|
||||
return "ANIAccessPointName"
|
||||
case DHCPv6OptANIAPBSSID:
|
||||
return "ANIAccessPointBSSID"
|
||||
case DHCPv6OptANIOperatorID:
|
||||
return "ANIOperatorIdentifier"
|
||||
case DHCPv6OptANIOperatorRealm:
|
||||
return "ANIOperatorRealm"
|
||||
case DHCPv6OptS46Priority:
|
||||
return "S64Priority"
|
||||
case DHCPv6OptMUDURLV6:
|
||||
return "ManufacturerUsageDescriptionURL"
|
||||
case DHCPv6OptV6Prefix64:
|
||||
return "V6Prefix64"
|
||||
case DHCPv6OptFBindingStatus:
|
||||
return "FailoverBindingStatus"
|
||||
case DHCPv6OptFConnectFlags:
|
||||
return "FailoverConnectFlags"
|
||||
case DHCPv6OptFDNSRemovalInfo:
|
||||
return "FailoverDNSRemovalInfo"
|
||||
case DHCPv6OptFDNSHostName:
|
||||
return "FailoverDNSHostName"
|
||||
case DHCPv6OptFDNSZoneName:
|
||||
return "FailoverDNSZoneName"
|
||||
case DHCPv6OptFDNSFlags:
|
||||
return "FailoverDNSFlags"
|
||||
case DHCPv6OptFExpirationTime:
|
||||
return "FailoverExpirationTime"
|
||||
case DHCPv6OptFMaxUnacknowledgedBNDUPD:
|
||||
return "FailoverMaxUnacknowledgedBNDUPDMessages"
|
||||
case DHCPv6OptFMCLT:
|
||||
return "FailoverMaximumClientLeadTime"
|
||||
case DHCPv6OptFPartnerLifetime:
|
||||
return "FailoverPartnerLifetime"
|
||||
case DHCPv6OptFPartnerLifetimeSent:
|
||||
return "FailoverPartnerLifetimeSent"
|
||||
case DHCPv6OptFPartnerDownTime:
|
||||
return "FailoverPartnerDownTime"
|
||||
case DHCPv6OptFPartnerRawCltTime:
|
||||
return "FailoverPartnerRawClientLeadTime"
|
||||
case DHCPv6OptFProtocolVersion:
|
||||
return "FailoverProtocolVersion"
|
||||
case DHCPv6OptFKeepaliveTime:
|
||||
return "FailoverKeepaliveTime"
|
||||
case DHCPv6OptFReconfigureData:
|
||||
return "FailoverReconfigureData"
|
||||
case DHCPv6OptFRelationshipName:
|
||||
return "FailoverRelationshipName"
|
||||
case DHCPv6OptFServerFlags:
|
||||
return "FailoverServerFlags"
|
||||
case DHCPv6OptFServerState:
|
||||
return "FailoverServerState"
|
||||
case DHCPv6OptFStartTimeOfState:
|
||||
return "FailoverStartTimeOfState"
|
||||
case DHCPv6OptFStateExpirationTime:
|
||||
return "FailoverStateExpirationTime"
|
||||
case DHCPv6OptRelayPort:
|
||||
return "RelayPort"
|
||||
case DHCPv6OptV6ZeroTouchRedirect:
|
||||
return "ZeroTouch"
|
||||
case DHCPv6OptIPV6AddressANDSF:
|
||||
return "ANDSFIPv6Address"
|
||||
default:
|
||||
return fmt.Sprintf("Unknown(%d)", uint16(o))
|
||||
}
|
||||
}
|
||||
|
||||
// DHCPv6Options is used to get nicely printed option lists which would normally
|
||||
// be cut off after 5 options.
|
||||
type DHCPv6Options []DHCPv6Option
|
||||
|
||||
// String returns a string version of the options list.
|
||||
func (o DHCPv6Options) String() string {
|
||||
buf := &bytes.Buffer{}
|
||||
buf.WriteByte('[')
|
||||
for i, opt := range o {
|
||||
buf.WriteString(opt.String())
|
||||
if i+1 != len(o) {
|
||||
buf.WriteString(", ")
|
||||
}
|
||||
}
|
||||
buf.WriteByte(']')
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
// DHCPv6Option rerpresents a DHCP option.
|
||||
type DHCPv6Option struct {
|
||||
Code DHCPv6Opt
|
||||
Length uint16
|
||||
Data []byte
|
||||
}
|
||||
|
||||
// String returns a string version of a DHCP Option.
|
||||
func (o DHCPv6Option) String() string {
|
||||
switch o.Code {
|
||||
case DHCPv6OptClientID, DHCPv6OptServerID:
|
||||
duid, err := decodeDHCPv6DUID(o.Data)
|
||||
if err != nil {
|
||||
return fmt.Sprintf("Option(%s:INVALID)", o.Code)
|
||||
}
|
||||
return fmt.Sprintf("Option(%s:[%s])", o.Code, duid.String())
|
||||
case DHCPv6OptOro:
|
||||
options := ""
|
||||
for i := 0; i < int(o.Length); i += 2 {
|
||||
if options != "" {
|
||||
options += ","
|
||||
}
|
||||
option := DHCPv6Opt(binary.BigEndian.Uint16(o.Data[i : i+2]))
|
||||
options += option.String()
|
||||
}
|
||||
return fmt.Sprintf("Option(%s:[%s])", o.Code, options)
|
||||
default:
|
||||
return fmt.Sprintf("Option(%s:%v)", o.Code, o.Data)
|
||||
}
|
||||
}
|
||||
|
||||
// NewDHCPv6Option constructs a new DHCPv6Option with a given type and data.
|
||||
func NewDHCPv6Option(code DHCPv6Opt, data []byte) DHCPv6Option {
|
||||
o := DHCPv6Option{Code: code}
|
||||
if data != nil {
|
||||
o.Data = data
|
||||
o.Length = uint16(len(data))
|
||||
}
|
||||
|
||||
return o
|
||||
}
|
||||
|
||||
func (o *DHCPv6Option) encode(b []byte, opts gopacket.SerializeOptions) error {
|
||||
binary.BigEndian.PutUint16(b[0:2], uint16(o.Code))
|
||||
if opts.FixLengths {
|
||||
binary.BigEndian.PutUint16(b[2:4], uint16(len(o.Data)))
|
||||
} else {
|
||||
binary.BigEndian.PutUint16(b[2:4], o.Length)
|
||||
}
|
||||
copy(b[4:], o.Data)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o *DHCPv6Option) decode(data []byte) error {
|
||||
if len(data) < 2 {
|
||||
return errors.New("not enough data to decode")
|
||||
}
|
||||
o.Code = DHCPv6Opt(binary.BigEndian.Uint16(data[0:2]))
|
||||
if len(data) < 3 {
|
||||
return errors.New("not enough data to decode")
|
||||
}
|
||||
o.Length = binary.BigEndian.Uint16(data[2:4])
|
||||
o.Data = data[4 : 4+o.Length]
|
||||
return nil
|
||||
}
|
||||
+1053
File diff suppressed because it is too large
Load Diff
+61
@@ -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
|
||||
+2105
File diff suppressed because it is too large
Load Diff
+71
@@ -0,0 +1,71 @@
|
||||
// Copyright 2012 Google, Inc. All rights reserved.
|
||||
// Copyright 2009-2011 Andreas Krennmair. All rights reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the LICENSE file in the root of the source
|
||||
// tree.
|
||||
|
||||
package layers
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"github.com/google/gopacket"
|
||||
)
|
||||
|
||||
// Dot1Q is the packet layer for 802.1Q VLAN headers.
|
||||
type Dot1Q struct {
|
||||
BaseLayer
|
||||
Priority uint8
|
||||
DropEligible bool
|
||||
VLANIdentifier uint16
|
||||
Type EthernetType
|
||||
}
|
||||
|
||||
// LayerType returns gopacket.LayerTypeDot1Q
|
||||
func (d *Dot1Q) LayerType() gopacket.LayerType { return LayerTypeDot1Q }
|
||||
|
||||
// DecodeFromBytes decodes the given bytes into this layer.
|
||||
func (d *Dot1Q) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
|
||||
d.Priority = (data[0] & 0xE0) >> 5
|
||||
d.DropEligible = data[0]&0x10 != 0
|
||||
d.VLANIdentifier = binary.BigEndian.Uint16(data[:2]) & 0x0FFF
|
||||
d.Type = EthernetType(binary.BigEndian.Uint16(data[2:4]))
|
||||
d.BaseLayer = BaseLayer{Contents: data[:4], Payload: data[4:]}
|
||||
return nil
|
||||
}
|
||||
|
||||
// CanDecode returns the set of layer types that this DecodingLayer can decode.
|
||||
func (d *Dot1Q) CanDecode() gopacket.LayerClass {
|
||||
return LayerTypeDot1Q
|
||||
}
|
||||
|
||||
// NextLayerType returns the layer type contained by this DecodingLayer.
|
||||
func (d *Dot1Q) NextLayerType() gopacket.LayerType {
|
||||
return d.Type.LayerType()
|
||||
}
|
||||
|
||||
func decodeDot1Q(data []byte, p gopacket.PacketBuilder) error {
|
||||
d := &Dot1Q{}
|
||||
return decodingLayerDecoder(d, data, p)
|
||||
}
|
||||
|
||||
// SerializeTo writes the serialized form of this layer into the
|
||||
// SerializationBuffer, implementing gopacket.SerializableLayer.
|
||||
// See the docs for gopacket.SerializableLayer for more info.
|
||||
func (d *Dot1Q) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
|
||||
bytes, err := b.PrependBytes(4)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if d.VLANIdentifier > 0xFFF {
|
||||
return fmt.Errorf("vlan identifier %v is too high", d.VLANIdentifier)
|
||||
}
|
||||
firstBytes := uint16(d.Priority)<<13 | d.VLANIdentifier
|
||||
if d.DropEligible {
|
||||
firstBytes |= 0x1000
|
||||
}
|
||||
binary.BigEndian.PutUint16(bytes, firstBytes)
|
||||
binary.BigEndian.PutUint16(bytes[2:], uint16(d.Type))
|
||||
return nil
|
||||
}
|
||||
+106
@@ -0,0 +1,106 @@
|
||||
// Copyright 2012 Google, Inc. All rights reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the LICENSE file in the root of the source
|
||||
// tree.
|
||||
|
||||
package layers
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"github.com/google/gopacket"
|
||||
)
|
||||
|
||||
type EAPCode uint8
|
||||
type EAPType uint8
|
||||
|
||||
const (
|
||||
EAPCodeRequest EAPCode = 1
|
||||
EAPCodeResponse EAPCode = 2
|
||||
EAPCodeSuccess EAPCode = 3
|
||||
EAPCodeFailure EAPCode = 4
|
||||
|
||||
// EAPTypeNone means that this EAP layer has no Type or TypeData.
|
||||
// Success and Failure EAPs will have this set.
|
||||
EAPTypeNone EAPType = 0
|
||||
|
||||
EAPTypeIdentity EAPType = 1
|
||||
EAPTypeNotification EAPType = 2
|
||||
EAPTypeNACK EAPType = 3
|
||||
EAPTypeOTP EAPType = 4
|
||||
EAPTypeTokenCard EAPType = 5
|
||||
)
|
||||
|
||||
// EAP defines an Extensible Authentication Protocol (rfc 3748) layer.
|
||||
type EAP struct {
|
||||
BaseLayer
|
||||
Code EAPCode
|
||||
Id uint8
|
||||
Length uint16
|
||||
Type EAPType
|
||||
TypeData []byte
|
||||
}
|
||||
|
||||
// LayerType returns LayerTypeEAP.
|
||||
func (e *EAP) LayerType() gopacket.LayerType { return LayerTypeEAP }
|
||||
|
||||
// DecodeFromBytes decodes the given bytes into this layer.
|
||||
func (e *EAP) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
|
||||
e.Code = EAPCode(data[0])
|
||||
e.Id = data[1]
|
||||
e.Length = binary.BigEndian.Uint16(data[2:4])
|
||||
switch {
|
||||
case e.Length > 4:
|
||||
e.Type = EAPType(data[4])
|
||||
e.TypeData = data[5:]
|
||||
case e.Length == 4:
|
||||
e.Type = 0
|
||||
e.TypeData = nil
|
||||
default:
|
||||
return fmt.Errorf("invalid EAP length %d", e.Length)
|
||||
}
|
||||
e.BaseLayer.Contents = data[:e.Length]
|
||||
e.BaseLayer.Payload = data[e.Length:] // Should be 0 bytes
|
||||
return nil
|
||||
}
|
||||
|
||||
// SerializeTo writes the serialized form of this layer into the
|
||||
// SerializationBuffer, implementing gopacket.SerializableLayer.
|
||||
// See the docs for gopacket.SerializableLayer for more info.
|
||||
func (e *EAP) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
|
||||
if opts.FixLengths {
|
||||
e.Length = uint16(len(e.TypeData) + 1)
|
||||
}
|
||||
size := len(e.TypeData) + 4
|
||||
if size > 4 {
|
||||
size++
|
||||
}
|
||||
bytes, err := b.PrependBytes(size)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
bytes[0] = byte(e.Code)
|
||||
bytes[1] = e.Id
|
||||
binary.BigEndian.PutUint16(bytes[2:], e.Length)
|
||||
if size > 4 {
|
||||
bytes[4] = byte(e.Type)
|
||||
copy(bytes[5:], e.TypeData)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// CanDecode returns the set of layer types that this DecodingLayer can decode.
|
||||
func (e *EAP) CanDecode() gopacket.LayerClass {
|
||||
return LayerTypeEAP
|
||||
}
|
||||
|
||||
// NextLayerType returns the layer type contained by this DecodingLayer.
|
||||
func (e *EAP) NextLayerType() gopacket.LayerType {
|
||||
return gopacket.LayerTypeZero
|
||||
}
|
||||
|
||||
func decodeEAP(data []byte, p gopacket.PacketBuilder) error {
|
||||
e := &EAP{}
|
||||
return decodingLayerDecoder(e, data, p)
|
||||
}
|
||||
+298
@@ -0,0 +1,298 @@
|
||||
// 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 {
|
||||
e.Version = data[0]
|
||||
e.Type = EAPOLType(data[1])
|
||||
e.Length = binary.BigEndian.Uint16(data[2:4])
|
||||
e.BaseLayer = BaseLayer{data[:4], data[4:]}
|
||||
return nil
|
||||
}
|
||||
|
||||
// SerializeTo writes the serialized form of this layer into the
|
||||
// SerializationBuffer, implementing gopacket.SerializableLayer
|
||||
func (e *EAPOL) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
|
||||
bytes, _ := b.PrependBytes(4)
|
||||
bytes[0] = e.Version
|
||||
bytes[1] = byte(e.Type)
|
||||
binary.BigEndian.PutUint16(bytes[2:], e.Length)
|
||||
return nil
|
||||
}
|
||||
|
||||
// CanDecode returns the set of layer types that this DecodingLayer can decode.
|
||||
func (e *EAPOL) CanDecode() gopacket.LayerClass {
|
||||
return LayerTypeEAPOL
|
||||
}
|
||||
|
||||
// NextLayerType returns the layer type contained by this DecodingLayer.
|
||||
func (e *EAPOL) NextLayerType() gopacket.LayerType {
|
||||
return e.Type.LayerType()
|
||||
}
|
||||
|
||||
func decodeEAPOL(data []byte, p gopacket.PacketBuilder) error {
|
||||
e := &EAPOL{}
|
||||
return decodingLayerDecoder(e, data, p)
|
||||
}
|
||||
|
||||
// EAPOLKeyDescriptorType is an enumeration of key descriptor types
|
||||
// as specified by 802.1x in the EAPOL-Key frame
|
||||
type EAPOLKeyDescriptorType uint8
|
||||
|
||||
// Enumeration of EAPOLKeyDescriptorType
|
||||
const (
|
||||
EAPOLKeyDescriptorTypeRC4 EAPOLKeyDescriptorType = 1
|
||||
EAPOLKeyDescriptorTypeDot11 EAPOLKeyDescriptorType = 2
|
||||
EAPOLKeyDescriptorTypeWPA EAPOLKeyDescriptorType = 254
|
||||
)
|
||||
|
||||
func (kdt EAPOLKeyDescriptorType) String() string {
|
||||
switch kdt {
|
||||
case EAPOLKeyDescriptorTypeRC4:
|
||||
return "RC4"
|
||||
case EAPOLKeyDescriptorTypeDot11:
|
||||
return "802.11"
|
||||
case EAPOLKeyDescriptorTypeWPA:
|
||||
return "WPA"
|
||||
default:
|
||||
return fmt.Sprintf("unknown descriptor type %d", kdt)
|
||||
}
|
||||
}
|
||||
|
||||
// EAPOLKeyDescriptorVersion is an enumeration of versions specifying the
|
||||
// encryption algorithm for the key data and the authentication for the
|
||||
// message integrity code (MIC)
|
||||
type EAPOLKeyDescriptorVersion uint8
|
||||
|
||||
// Enumeration of EAPOLKeyDescriptorVersion
|
||||
const (
|
||||
EAPOLKeyDescriptorVersionOther EAPOLKeyDescriptorVersion = 0
|
||||
EAPOLKeyDescriptorVersionRC4HMACMD5 EAPOLKeyDescriptorVersion = 1
|
||||
EAPOLKeyDescriptorVersionAESHMACSHA1 EAPOLKeyDescriptorVersion = 2
|
||||
EAPOLKeyDescriptorVersionAES128CMAC EAPOLKeyDescriptorVersion = 3
|
||||
)
|
||||
|
||||
func (v EAPOLKeyDescriptorVersion) String() string {
|
||||
switch v {
|
||||
case EAPOLKeyDescriptorVersionOther:
|
||||
return "Other"
|
||||
case EAPOLKeyDescriptorVersionRC4HMACMD5:
|
||||
return "RC4-HMAC-MD5"
|
||||
case EAPOLKeyDescriptorVersionAESHMACSHA1:
|
||||
return "AES-HMAC-SHA1-128"
|
||||
case EAPOLKeyDescriptorVersionAES128CMAC:
|
||||
return "AES-128-CMAC"
|
||||
default:
|
||||
return fmt.Sprintf("unknown version %d", v)
|
||||
}
|
||||
}
|
||||
|
||||
// EAPOLKeyType is an enumeration of key derivation types describing
|
||||
// the purpose of the keys being derived.
|
||||
type EAPOLKeyType uint8
|
||||
|
||||
// Enumeration of EAPOLKeyType
|
||||
const (
|
||||
EAPOLKeyTypeGroupSMK EAPOLKeyType = 0
|
||||
EAPOLKeyTypePairwise EAPOLKeyType = 1
|
||||
)
|
||||
|
||||
func (kt EAPOLKeyType) String() string {
|
||||
switch kt {
|
||||
case EAPOLKeyTypeGroupSMK:
|
||||
return "Group/SMK"
|
||||
case EAPOLKeyTypePairwise:
|
||||
return "Pairwise"
|
||||
default:
|
||||
return fmt.Sprintf("unknown key type %d", kt)
|
||||
}
|
||||
}
|
||||
|
||||
// EAPOLKey defines an EAPOL-Key frame for 802.1x authentication
|
||||
type EAPOLKey struct {
|
||||
BaseLayer
|
||||
KeyDescriptorType EAPOLKeyDescriptorType
|
||||
KeyDescriptorVersion EAPOLKeyDescriptorVersion
|
||||
KeyType EAPOLKeyType
|
||||
KeyIndex uint8
|
||||
Install bool
|
||||
KeyACK bool
|
||||
KeyMIC bool
|
||||
Secure bool
|
||||
MICError bool
|
||||
Request bool
|
||||
HasEncryptedKeyData bool
|
||||
SMKMessage bool
|
||||
KeyLength uint16
|
||||
ReplayCounter uint64
|
||||
Nonce []byte
|
||||
IV []byte
|
||||
RSC uint64
|
||||
ID uint64
|
||||
MIC []byte
|
||||
KeyDataLength uint16
|
||||
EncryptedKeyData []byte
|
||||
}
|
||||
|
||||
// LayerType returns LayerTypeEAPOLKey.
|
||||
func (ek *EAPOLKey) LayerType() gopacket.LayerType {
|
||||
return LayerTypeEAPOLKey
|
||||
}
|
||||
|
||||
// CanDecode returns the set of layer types that this DecodingLayer can decode.
|
||||
func (ek *EAPOLKey) CanDecode() gopacket.LayerType {
|
||||
return LayerTypeEAPOLKey
|
||||
}
|
||||
|
||||
// NextLayerType returns layers.LayerTypeDot11InformationElement if the key
|
||||
// data exists and is unencrypted, otherwise it does not expect a next layer.
|
||||
func (ek *EAPOLKey) NextLayerType() gopacket.LayerType {
|
||||
if !ek.HasEncryptedKeyData && ek.KeyDataLength > 0 {
|
||||
return LayerTypeDot11InformationElement
|
||||
}
|
||||
return gopacket.LayerTypePayload
|
||||
}
|
||||
|
||||
const eapolKeyFrameLen = 95
|
||||
|
||||
// DecodeFromBytes decodes the given bytes into this layer.
|
||||
func (ek *EAPOLKey) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
|
||||
if len(data) < eapolKeyFrameLen {
|
||||
df.SetTruncated()
|
||||
return fmt.Errorf("EAPOLKey length %v too short, %v required",
|
||||
len(data), eapolKeyFrameLen)
|
||||
}
|
||||
|
||||
ek.KeyDescriptorType = EAPOLKeyDescriptorType(data[0])
|
||||
|
||||
info := binary.BigEndian.Uint16(data[1:3])
|
||||
ek.KeyDescriptorVersion = EAPOLKeyDescriptorVersion(info & 0x0007)
|
||||
ek.KeyType = EAPOLKeyType((info & 0x0008) >> 3)
|
||||
ek.KeyIndex = uint8((info & 0x0030) >> 4)
|
||||
ek.Install = (info & 0x0040) != 0
|
||||
ek.KeyACK = (info & 0x0080) != 0
|
||||
ek.KeyMIC = (info & 0x0100) != 0
|
||||
ek.Secure = (info & 0x0200) != 0
|
||||
ek.MICError = (info & 0x0400) != 0
|
||||
ek.Request = (info & 0x0800) != 0
|
||||
ek.HasEncryptedKeyData = (info & 0x1000) != 0
|
||||
ek.SMKMessage = (info & 0x2000) != 0
|
||||
|
||||
ek.KeyLength = binary.BigEndian.Uint16(data[3:5])
|
||||
ek.ReplayCounter = binary.BigEndian.Uint64(data[5:13])
|
||||
|
||||
ek.Nonce = data[13:45]
|
||||
ek.IV = data[45:61]
|
||||
ek.RSC = binary.BigEndian.Uint64(data[61:69])
|
||||
ek.ID = binary.BigEndian.Uint64(data[69:77])
|
||||
ek.MIC = data[77:93]
|
||||
|
||||
ek.KeyDataLength = binary.BigEndian.Uint16(data[93:95])
|
||||
|
||||
totalLength := eapolKeyFrameLen + int(ek.KeyDataLength)
|
||||
if len(data) < totalLength {
|
||||
df.SetTruncated()
|
||||
return fmt.Errorf("EAPOLKey data length %d too short, %d required",
|
||||
len(data)-eapolKeyFrameLen, ek.KeyDataLength)
|
||||
}
|
||||
|
||||
if ek.HasEncryptedKeyData {
|
||||
ek.EncryptedKeyData = data[eapolKeyFrameLen:totalLength]
|
||||
ek.BaseLayer = BaseLayer{
|
||||
Contents: data[:totalLength],
|
||||
Payload: data[totalLength:],
|
||||
}
|
||||
} else {
|
||||
ek.BaseLayer = BaseLayer{
|
||||
Contents: data[:eapolKeyFrameLen],
|
||||
Payload: data[eapolKeyFrameLen:],
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// SerializeTo writes the serialized form of this layer into the
|
||||
// SerializationBuffer, implementing gopacket.SerializableLayer.
|
||||
// See the docs for gopacket.SerializableLayer for more info.
|
||||
func (ek *EAPOLKey) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
|
||||
buf, err := b.PrependBytes(eapolKeyFrameLen + len(ek.EncryptedKeyData))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
buf[0] = byte(ek.KeyDescriptorType)
|
||||
|
||||
var info uint16
|
||||
info |= uint16(ek.KeyDescriptorVersion)
|
||||
info |= uint16(ek.KeyType) << 3
|
||||
info |= uint16(ek.KeyIndex) << 4
|
||||
if ek.Install {
|
||||
info |= 0x0040
|
||||
}
|
||||
if ek.KeyACK {
|
||||
info |= 0x0080
|
||||
}
|
||||
if ek.KeyMIC {
|
||||
info |= 0x0100
|
||||
}
|
||||
if ek.Secure {
|
||||
info |= 0x0200
|
||||
}
|
||||
if ek.MICError {
|
||||
info |= 0x0400
|
||||
}
|
||||
if ek.Request {
|
||||
info |= 0x0800
|
||||
}
|
||||
if ek.HasEncryptedKeyData {
|
||||
info |= 0x1000
|
||||
}
|
||||
if ek.SMKMessage {
|
||||
info |= 0x2000
|
||||
}
|
||||
binary.BigEndian.PutUint16(buf[1:3], info)
|
||||
|
||||
binary.BigEndian.PutUint16(buf[3:5], ek.KeyLength)
|
||||
binary.BigEndian.PutUint64(buf[5:13], ek.ReplayCounter)
|
||||
|
||||
copy(buf[13:45], ek.Nonce)
|
||||
copy(buf[45:61], ek.IV)
|
||||
binary.BigEndian.PutUint64(buf[61:69], ek.RSC)
|
||||
binary.BigEndian.PutUint64(buf[69:77], ek.ID)
|
||||
copy(buf[77:93], ek.MIC)
|
||||
|
||||
binary.BigEndian.PutUint16(buf[93:95], ek.KeyDataLength)
|
||||
if len(ek.EncryptedKeyData) > 0 {
|
||||
copy(buf[95:95+len(ek.EncryptedKeyData)], ek.EncryptedKeyData)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func decodeEAPOLKey(data []byte, p gopacket.PacketBuilder) error {
|
||||
ek := &EAPOLKey{}
|
||||
return decodingLayerDecoder(ek, data, p)
|
||||
}
|
||||
+97
@@ -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))
|
||||
}
|
||||
+448
@@ -0,0 +1,448 @@
|
||||
// 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"
|
||||
"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
|
||||
}
|
||||
|
||||
// errorFunc returns a decoder that spits out a specific error message.
|
||||
func errorFunc(msg string) gopacket.Decoder {
|
||||
var e = errors.New(msg)
|
||||
return gopacket.DecodeFunc(func([]byte, gopacket.PacketBuilder) error {
|
||||
return e
|
||||
})
|
||||
}
|
||||
|
||||
// EthernetType is an enumeration of ethernet type values, and acts as a decoder
|
||||
// for any type it supports.
|
||||
type EthernetType uint16
|
||||
|
||||
const (
|
||||
// EthernetTypeLLC is not an actual ethernet type. It is instead a
|
||||
// placeholder we use in Ethernet frames that use the 802.3 standard of
|
||||
// srcmac|dstmac|length|LLC instead of srcmac|dstmac|ethertype.
|
||||
EthernetTypeLLC EthernetType = 0
|
||||
EthernetTypeIPv4 EthernetType = 0x0800
|
||||
EthernetTypeARP EthernetType = 0x0806
|
||||
EthernetTypeIPv6 EthernetType = 0x86DD
|
||||
EthernetTypeCiscoDiscovery EthernetType = 0x2000
|
||||
EthernetTypeNortelDiscovery EthernetType = 0x01a2
|
||||
EthernetTypeTransparentEthernetBridging EthernetType = 0x6558
|
||||
EthernetTypeDot1Q EthernetType = 0x8100
|
||||
EthernetTypePPP EthernetType = 0x880b
|
||||
EthernetTypePPPoEDiscovery EthernetType = 0x8863
|
||||
EthernetTypePPPoESession EthernetType = 0x8864
|
||||
EthernetTypeMPLSUnicast EthernetType = 0x8847
|
||||
EthernetTypeMPLSMulticast EthernetType = 0x8848
|
||||
EthernetTypeEAPOL EthernetType = 0x888e
|
||||
EthernetTypeQinQ EthernetType = 0x88a8
|
||||
EthernetTypeLinkLayerDiscovery EthernetType = 0x88cc
|
||||
EthernetTypeEthernetCTP EthernetType = 0x9000
|
||||
)
|
||||
|
||||
// IPProtocol is an enumeration of IP protocol values, and acts as a decoder
|
||||
// for any type it supports.
|
||||
type IPProtocol uint8
|
||||
|
||||
const (
|
||||
IPProtocolIPv6HopByHop IPProtocol = 0
|
||||
IPProtocolICMPv4 IPProtocol = 1
|
||||
IPProtocolIGMP IPProtocol = 2
|
||||
IPProtocolIPv4 IPProtocol = 4
|
||||
IPProtocolTCP IPProtocol = 6
|
||||
IPProtocolUDP IPProtocol = 17
|
||||
IPProtocolRUDP IPProtocol = 27
|
||||
IPProtocolIPv6 IPProtocol = 41
|
||||
IPProtocolIPv6Routing IPProtocol = 43
|
||||
IPProtocolIPv6Fragment IPProtocol = 44
|
||||
IPProtocolGRE IPProtocol = 47
|
||||
IPProtocolESP IPProtocol = 50
|
||||
IPProtocolAH IPProtocol = 51
|
||||
IPProtocolICMPv6 IPProtocol = 58
|
||||
IPProtocolNoNextHeader IPProtocol = 59
|
||||
IPProtocolIPv6Destination IPProtocol = 60
|
||||
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
|
||||
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}
|
||||
|
||||
IPProtocolMetadata[IPProtocolIPv4] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeIPv4), Name: "IPv4", LayerType: LayerTypeIPv4}
|
||||
IPProtocolMetadata[IPProtocolTCP] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeTCP), Name: "TCP", LayerType: LayerTypeTCP}
|
||||
IPProtocolMetadata[IPProtocolUDP] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeUDP), Name: "UDP", LayerType: LayerTypeUDP}
|
||||
IPProtocolMetadata[IPProtocolICMPv4] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeICMPv4), Name: "ICMPv4", LayerType: LayerTypeICMPv4}
|
||||
IPProtocolMetadata[IPProtocolICMPv6] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeICMPv6), Name: "ICMPv6", LayerType: LayerTypeICMPv6}
|
||||
IPProtocolMetadata[IPProtocolSCTP] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeSCTP), Name: "SCTP", LayerType: LayerTypeSCTP}
|
||||
IPProtocolMetadata[IPProtocolIPv6] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeIPv6), Name: "IPv6", LayerType: LayerTypeIPv6}
|
||||
IPProtocolMetadata[IPProtocolIPIP] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeIPv4), Name: "IPv4", LayerType: LayerTypeIPv4}
|
||||
IPProtocolMetadata[IPProtocolEtherIP] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeEtherIP), Name: "EtherIP", LayerType: LayerTypeEtherIP}
|
||||
IPProtocolMetadata[IPProtocolRUDP] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeRUDP), Name: "RUDP", LayerType: LayerTypeRUDP}
|
||||
IPProtocolMetadata[IPProtocolGRE] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeGRE), Name: "GRE", LayerType: LayerTypeGRE}
|
||||
IPProtocolMetadata[IPProtocolIPv6HopByHop] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeIPv6HopByHop), Name: "IPv6HopByHop", LayerType: LayerTypeIPv6HopByHop}
|
||||
IPProtocolMetadata[IPProtocolIPv6Routing] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeIPv6Routing), Name: "IPv6Routing", LayerType: LayerTypeIPv6Routing}
|
||||
IPProtocolMetadata[IPProtocolIPv6Fragment] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeIPv6Fragment), Name: "IPv6Fragment", LayerType: LayerTypeIPv6Fragment}
|
||||
IPProtocolMetadata[IPProtocolIPv6Destination] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeIPv6Destination), Name: "IPv6Destination", LayerType: LayerTypeIPv6Destination}
|
||||
IPProtocolMetadata[IPProtocolOSPF] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeOSPF), Name: "OSPF", LayerType: LayerTypeOSPF}
|
||||
IPProtocolMetadata[IPProtocolAH] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeIPSecAH), Name: "IPSecAH", LayerType: LayerTypeIPSecAH}
|
||||
IPProtocolMetadata[IPProtocolESP] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeIPSecESP), Name: "IPSecESP", LayerType: LayerTypeIPSecESP}
|
||||
IPProtocolMetadata[IPProtocolUDPLite] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeUDPLite), Name: "UDPLite", LayerType: LayerTypeUDPLite}
|
||||
IPProtocolMetadata[IPProtocolMPLSInIP] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeMPLS), Name: "MPLS", LayerType: LayerTypeMPLS}
|
||||
IPProtocolMetadata[IPProtocolNoNextHeader] = EnumMetadata{DecodeWith: gopacket.DecodePayload, Name: "NoNextHeader", LayerType: gopacket.LayerTypePayload}
|
||||
IPProtocolMetadata[IPProtocolIGMP] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeIGMP), Name: "IGMP", LayerType: LayerTypeIGMP}
|
||||
IPProtocolMetadata[IPProtocolVRRP] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeVRRP), Name: "VRRP", LayerType: LayerTypeVRRP}
|
||||
|
||||
SCTPChunkTypeMetadata[SCTPChunkTypeData] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeSCTPData), Name: "Data"}
|
||||
SCTPChunkTypeMetadata[SCTPChunkTypeInit] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeSCTPInit), Name: "Init"}
|
||||
SCTPChunkTypeMetadata[SCTPChunkTypeInitAck] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeSCTPInit), Name: "InitAck"}
|
||||
SCTPChunkTypeMetadata[SCTPChunkTypeSack] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeSCTPSack), Name: "Sack"}
|
||||
SCTPChunkTypeMetadata[SCTPChunkTypeHeartbeat] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeSCTPHeartbeat), Name: "Heartbeat"}
|
||||
SCTPChunkTypeMetadata[SCTPChunkTypeHeartbeatAck] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeSCTPHeartbeat), Name: "HeartbeatAck"}
|
||||
SCTPChunkTypeMetadata[SCTPChunkTypeAbort] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeSCTPError), Name: "Abort"}
|
||||
SCTPChunkTypeMetadata[SCTPChunkTypeError] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeSCTPError), Name: "Error"}
|
||||
SCTPChunkTypeMetadata[SCTPChunkTypeShutdown] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeSCTPShutdown), Name: "Shutdown"}
|
||||
SCTPChunkTypeMetadata[SCTPChunkTypeShutdownAck] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeSCTPShutdownAck), Name: "ShutdownAck"}
|
||||
SCTPChunkTypeMetadata[SCTPChunkTypeCookieEcho] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeSCTPCookieEcho), Name: "CookieEcho"}
|
||||
SCTPChunkTypeMetadata[SCTPChunkTypeCookieAck] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeSCTPEmptyLayer), Name: "CookieAck"}
|
||||
SCTPChunkTypeMetadata[SCTPChunkTypeShutdownComplete] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeSCTPEmptyLayer), Name: "ShutdownComplete"}
|
||||
|
||||
PPPTypeMetadata[PPPTypeIPv4] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeIPv4), Name: "IPv4"}
|
||||
PPPTypeMetadata[PPPTypeIPv6] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeIPv6), Name: "IPv6"}
|
||||
PPPTypeMetadata[PPPTypeMPLSUnicast] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeMPLS), Name: "MPLSUnicast"}
|
||||
PPPTypeMetadata[PPPTypeMPLSMulticast] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeMPLS), Name: "MPLSMulticast"}
|
||||
|
||||
PPPoECodeMetadata[PPPoECodeSession] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodePPP), Name: "PPP"}
|
||||
|
||||
LinkTypeMetadata[LinkTypeEthernet] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeEthernet), Name: "Ethernet"}
|
||||
LinkTypeMetadata[LinkTypePPP] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodePPP), Name: "PPP"}
|
||||
LinkTypeMetadata[LinkTypeFDDI] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeFDDI), Name: "FDDI"}
|
||||
LinkTypeMetadata[LinkTypeNull] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeLoopback), Name: "Null"}
|
||||
LinkTypeMetadata[LinkTypeIEEE802_11] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11), Name: "Dot11"}
|
||||
LinkTypeMetadata[LinkTypeLoop] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeLoopback), Name: "Loop"}
|
||||
LinkTypeMetadata[LinkTypeIEEE802_11] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11), Name: "802.11"}
|
||||
LinkTypeMetadata[LinkTypeRaw] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeIPv4or6), Name: "Raw"}
|
||||
// See https://github.com/the-tcpdump-group/libpcap/blob/170f717e6e818cdc4bcbbfd906b63088eaa88fa0/pcap/dlt.h#L85
|
||||
// Or https://github.com/wireshark/wireshark/blob/854cfe53efe44080609c78053ecfb2342ad84a08/wiretap/pcap-common.c#L508
|
||||
if runtime.GOOS == "openbsd" {
|
||||
LinkTypeMetadata[14] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeIPv4or6), Name: "Raw"}
|
||||
} else {
|
||||
LinkTypeMetadata[12] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeIPv4or6), Name: "Raw"}
|
||||
}
|
||||
LinkTypeMetadata[LinkTypePFLog] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodePFLog), Name: "PFLog"}
|
||||
LinkTypeMetadata[LinkTypeIEEE80211Radio] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeRadioTap), Name: "RadioTap"}
|
||||
LinkTypeMetadata[LinkTypeLinuxUSB] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeUSB), Name: "USB"}
|
||||
LinkTypeMetadata[LinkTypeLinuxSLL] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeLinuxSLL), Name: "Linux SLL"}
|
||||
LinkTypeMetadata[LinkTypePrismHeader] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodePrismHeader), Name: "Prism"}
|
||||
|
||||
FDDIFrameControlMetadata[FDDIFrameControlLLC] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeLLC), Name: "LLC"}
|
||||
|
||||
EAPOLTypeMetadata[EAPOLTypeEAP] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeEAP), Name: "EAP", LayerType: LayerTypeEAP}
|
||||
EAPOLTypeMetadata[EAPOLTypeKey] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeEAPOLKey), Name: "EAPOLKey", LayerType: LayerTypeEAPOLKey}
|
||||
|
||||
ProtocolFamilyMetadata[ProtocolFamilyIPv4] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeIPv4), Name: "IPv4", LayerType: LayerTypeIPv4}
|
||||
ProtocolFamilyMetadata[ProtocolFamilyIPv6BSD] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeIPv6), Name: "IPv6", LayerType: LayerTypeIPv6}
|
||||
ProtocolFamilyMetadata[ProtocolFamilyIPv6FreeBSD] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeIPv6), Name: "IPv6", LayerType: LayerTypeIPv6}
|
||||
ProtocolFamilyMetadata[ProtocolFamilyIPv6Darwin] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeIPv6), Name: "IPv6", LayerType: LayerTypeIPv6}
|
||||
ProtocolFamilyMetadata[ProtocolFamilyIPv6Linux] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeIPv6), Name: "IPv6", LayerType: LayerTypeIPv6}
|
||||
|
||||
Dot11TypeMetadata[Dot11TypeMgmtAssociationReq] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11MgmtAssociationReq), Name: "MgmtAssociationReq", LayerType: LayerTypeDot11MgmtAssociationReq}
|
||||
Dot11TypeMetadata[Dot11TypeMgmtAssociationResp] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11MgmtAssociationResp), Name: "MgmtAssociationResp", LayerType: LayerTypeDot11MgmtAssociationResp}
|
||||
Dot11TypeMetadata[Dot11TypeMgmtReassociationReq] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11MgmtReassociationReq), Name: "MgmtReassociationReq", LayerType: LayerTypeDot11MgmtReassociationReq}
|
||||
Dot11TypeMetadata[Dot11TypeMgmtReassociationResp] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11MgmtReassociationResp), Name: "MgmtReassociationResp", LayerType: LayerTypeDot11MgmtReassociationResp}
|
||||
Dot11TypeMetadata[Dot11TypeMgmtProbeReq] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11MgmtProbeReq), Name: "MgmtProbeReq", LayerType: LayerTypeDot11MgmtProbeReq}
|
||||
Dot11TypeMetadata[Dot11TypeMgmtProbeResp] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11MgmtProbeResp), Name: "MgmtProbeResp", LayerType: LayerTypeDot11MgmtProbeResp}
|
||||
Dot11TypeMetadata[Dot11TypeMgmtMeasurementPilot] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11MgmtMeasurementPilot), Name: "MgmtMeasurementPilot", LayerType: LayerTypeDot11MgmtMeasurementPilot}
|
||||
Dot11TypeMetadata[Dot11TypeMgmtBeacon] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11MgmtBeacon), Name: "MgmtBeacon", LayerType: LayerTypeDot11MgmtBeacon}
|
||||
Dot11TypeMetadata[Dot11TypeMgmtATIM] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11MgmtATIM), Name: "MgmtATIM", LayerType: LayerTypeDot11MgmtATIM}
|
||||
Dot11TypeMetadata[Dot11TypeMgmtDisassociation] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11MgmtDisassociation), Name: "MgmtDisassociation", LayerType: LayerTypeDot11MgmtDisassociation}
|
||||
Dot11TypeMetadata[Dot11TypeMgmtAuthentication] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11MgmtAuthentication), Name: "MgmtAuthentication", LayerType: LayerTypeDot11MgmtAuthentication}
|
||||
Dot11TypeMetadata[Dot11TypeMgmtDeauthentication] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11MgmtDeauthentication), Name: "MgmtDeauthentication", LayerType: LayerTypeDot11MgmtDeauthentication}
|
||||
Dot11TypeMetadata[Dot11TypeMgmtAction] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11MgmtAction), Name: "MgmtAction", LayerType: LayerTypeDot11MgmtAction}
|
||||
Dot11TypeMetadata[Dot11TypeMgmtActionNoAck] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11MgmtActionNoAck), Name: "MgmtActionNoAck", LayerType: LayerTypeDot11MgmtActionNoAck}
|
||||
Dot11TypeMetadata[Dot11TypeCtrl] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11Ctrl), Name: "Ctrl", LayerType: LayerTypeDot11Ctrl}
|
||||
Dot11TypeMetadata[Dot11TypeCtrlWrapper] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11Ctrl), Name: "CtrlWrapper", LayerType: LayerTypeDot11Ctrl}
|
||||
Dot11TypeMetadata[Dot11TypeCtrlBlockAckReq] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11CtrlBlockAckReq), Name: "CtrlBlockAckReq", LayerType: LayerTypeDot11CtrlBlockAckReq}
|
||||
Dot11TypeMetadata[Dot11TypeCtrlBlockAck] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11CtrlBlockAck), Name: "CtrlBlockAck", LayerType: LayerTypeDot11CtrlBlockAck}
|
||||
Dot11TypeMetadata[Dot11TypeCtrlPowersavePoll] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11CtrlPowersavePoll), Name: "CtrlPowersavePoll", LayerType: LayerTypeDot11CtrlPowersavePoll}
|
||||
Dot11TypeMetadata[Dot11TypeCtrlRTS] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11CtrlRTS), Name: "CtrlRTS", LayerType: LayerTypeDot11CtrlRTS}
|
||||
Dot11TypeMetadata[Dot11TypeCtrlCTS] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11CtrlCTS), Name: "CtrlCTS", LayerType: LayerTypeDot11CtrlCTS}
|
||||
Dot11TypeMetadata[Dot11TypeCtrlAck] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11CtrlAck), Name: "CtrlAck", LayerType: LayerTypeDot11CtrlAck}
|
||||
Dot11TypeMetadata[Dot11TypeCtrlCFEnd] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11CtrlCFEnd), Name: "CtrlCFEnd", LayerType: LayerTypeDot11CtrlCFEnd}
|
||||
Dot11TypeMetadata[Dot11TypeCtrlCFEndAck] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11CtrlCFEndAck), Name: "CtrlCFEndAck", LayerType: LayerTypeDot11CtrlCFEndAck}
|
||||
Dot11TypeMetadata[Dot11TypeData] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11Data), Name: "Data", LayerType: LayerTypeDot11Data}
|
||||
Dot11TypeMetadata[Dot11TypeDataCFAck] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11DataCFAck), Name: "DataCFAck", LayerType: LayerTypeDot11DataCFAck}
|
||||
Dot11TypeMetadata[Dot11TypeDataCFPoll] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11DataCFPoll), Name: "DataCFPoll", LayerType: LayerTypeDot11DataCFPoll}
|
||||
Dot11TypeMetadata[Dot11TypeDataCFAckPoll] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11DataCFAckPoll), Name: "DataCFAckPoll", LayerType: LayerTypeDot11DataCFAckPoll}
|
||||
Dot11TypeMetadata[Dot11TypeDataNull] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11DataNull), Name: "DataNull", LayerType: LayerTypeDot11DataNull}
|
||||
Dot11TypeMetadata[Dot11TypeDataCFAckNoData] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11DataCFAckNoData), Name: "DataCFAckNoData", LayerType: LayerTypeDot11DataCFAckNoData}
|
||||
Dot11TypeMetadata[Dot11TypeDataCFPollNoData] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11DataCFPollNoData), Name: "DataCFPollNoData", LayerType: LayerTypeDot11DataCFPollNoData}
|
||||
Dot11TypeMetadata[Dot11TypeDataCFAckPollNoData] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11DataCFAckPollNoData), Name: "DataCFAckPollNoData", LayerType: LayerTypeDot11DataCFAckPollNoData}
|
||||
Dot11TypeMetadata[Dot11TypeDataQOSData] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11DataQOSData), Name: "DataQOSData", LayerType: LayerTypeDot11DataQOSData}
|
||||
Dot11TypeMetadata[Dot11TypeDataQOSDataCFAck] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11DataQOSDataCFAck), Name: "DataQOSDataCFAck", LayerType: LayerTypeDot11DataQOSDataCFAck}
|
||||
Dot11TypeMetadata[Dot11TypeDataQOSDataCFPoll] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11DataQOSDataCFPoll), Name: "DataQOSDataCFPoll", LayerType: LayerTypeDot11DataQOSDataCFPoll}
|
||||
Dot11TypeMetadata[Dot11TypeDataQOSDataCFAckPoll] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11DataQOSDataCFAckPoll), Name: "DataQOSDataCFAckPoll", LayerType: LayerTypeDot11DataQOSDataCFAckPoll}
|
||||
Dot11TypeMetadata[Dot11TypeDataQOSNull] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11DataQOSNull), Name: "DataQOSNull", LayerType: LayerTypeDot11DataQOSNull}
|
||||
Dot11TypeMetadata[Dot11TypeDataQOSCFPollNoData] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11DataQOSCFPollNoData), Name: "DataQOSCFPollNoData", LayerType: LayerTypeDot11DataQOSCFPollNoData}
|
||||
Dot11TypeMetadata[Dot11TypeDataQOSCFAckPollNoData] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeDot11DataQOSCFAckPollNoData), Name: "DataQOSCFAckPollNoData", LayerType: LayerTypeDot11DataQOSCFAckPollNoData}
|
||||
|
||||
USBTransportTypeMetadata[USBTransportTypeInterrupt] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeUSBInterrupt), Name: "Interrupt", LayerType: LayerTypeUSBInterrupt}
|
||||
USBTransportTypeMetadata[USBTransportTypeControl] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeUSBControl), Name: "Control", LayerType: LayerTypeUSBControl}
|
||||
USBTransportTypeMetadata[USBTransportTypeBulk] = EnumMetadata{DecodeWith: gopacket.DecodeFunc(decodeUSBBulk), Name: "Bulk", LayerType: LayerTypeUSBBulk}
|
||||
}
|
||||
+434
@@ -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",
|
||||
}
|
||||
}
|
||||
}
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
// Copyright 2012 Google, Inc. All rights reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the LICENSE file in the root of the source
|
||||
// tree.
|
||||
|
||||
package layers
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"github.com/google/gopacket"
|
||||
)
|
||||
|
||||
// EtherIP is the struct for storing RFC 3378 EtherIP packet headers.
|
||||
type EtherIP struct {
|
||||
BaseLayer
|
||||
Version uint8
|
||||
Reserved uint16
|
||||
}
|
||||
|
||||
// LayerType returns gopacket.LayerTypeEtherIP.
|
||||
func (e *EtherIP) LayerType() gopacket.LayerType { return LayerTypeEtherIP }
|
||||
|
||||
// DecodeFromBytes decodes the given bytes into this layer.
|
||||
func (e *EtherIP) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
|
||||
e.Version = data[0] >> 4
|
||||
e.Reserved = binary.BigEndian.Uint16(data[:2]) & 0x0fff
|
||||
e.BaseLayer = BaseLayer{data[:2], data[2:]}
|
||||
return nil
|
||||
}
|
||||
|
||||
// CanDecode returns the set of layer types that this DecodingLayer can decode.
|
||||
func (e *EtherIP) CanDecode() gopacket.LayerClass {
|
||||
return LayerTypeEtherIP
|
||||
}
|
||||
|
||||
// NextLayerType returns the layer type contained by this DecodingLayer.
|
||||
func (e *EtherIP) NextLayerType() gopacket.LayerType {
|
||||
return LayerTypeEthernet
|
||||
}
|
||||
|
||||
func decodeEtherIP(data []byte, p gopacket.PacketBuilder) error {
|
||||
e := &EtherIP{}
|
||||
return decodingLayerDecoder(e, data, p)
|
||||
}
|
||||
+123
@@ -0,0 +1,123 @@
|
||||
// Copyright 2012 Google, Inc. All rights reserved.
|
||||
// Copyright 2009-2011 Andreas Krennmair. All rights reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the LICENSE file in the root of the source
|
||||
// tree.
|
||||
|
||||
package layers
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/google/gopacket"
|
||||
"net"
|
||||
)
|
||||
|
||||
// EthernetBroadcast is the broadcast MAC address used by Ethernet.
|
||||
var EthernetBroadcast = net.HardwareAddr{0xff, 0xff, 0xff, 0xff, 0xff, 0xff}
|
||||
|
||||
// Ethernet is the layer for Ethernet frame headers.
|
||||
type Ethernet struct {
|
||||
BaseLayer
|
||||
SrcMAC, DstMAC net.HardwareAddr
|
||||
EthernetType EthernetType
|
||||
// Length is only set if a length field exists within this header. Ethernet
|
||||
// headers follow two different standards, one that uses an EthernetType, the
|
||||
// other which defines a length the follows with a LLC header (802.3). If the
|
||||
// former is the case, we set EthernetType and Length stays 0. In the latter
|
||||
// case, we set Length and EthernetType = EthernetTypeLLC.
|
||||
Length uint16
|
||||
}
|
||||
|
||||
// LayerType returns LayerTypeEthernet
|
||||
func (e *Ethernet) LayerType() gopacket.LayerType { return LayerTypeEthernet }
|
||||
|
||||
func (e *Ethernet) LinkFlow() gopacket.Flow {
|
||||
return gopacket.NewFlow(EndpointMAC, e.SrcMAC, e.DstMAC)
|
||||
}
|
||||
|
||||
func (eth *Ethernet) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
|
||||
if len(data) < 14 {
|
||||
return errors.New("Ethernet packet too small")
|
||||
}
|
||||
eth.DstMAC = net.HardwareAddr(data[0:6])
|
||||
eth.SrcMAC = net.HardwareAddr(data[6:12])
|
||||
eth.EthernetType = EthernetType(binary.BigEndian.Uint16(data[12:14]))
|
||||
eth.BaseLayer = BaseLayer{data[:14], data[14:]}
|
||||
eth.Length = 0
|
||||
if eth.EthernetType < 0x0600 {
|
||||
eth.Length = uint16(eth.EthernetType)
|
||||
eth.EthernetType = EthernetTypeLLC
|
||||
if cmp := len(eth.Payload) - int(eth.Length); cmp < 0 {
|
||||
df.SetTruncated()
|
||||
} else if cmp > 0 {
|
||||
// Strip off bytes at the end, since we have too many bytes
|
||||
eth.Payload = eth.Payload[:len(eth.Payload)-cmp]
|
||||
}
|
||||
// fmt.Println(eth)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// SerializeTo writes the serialized form of this layer into the
|
||||
// SerializationBuffer, implementing gopacket.SerializableLayer.
|
||||
// See the docs for gopacket.SerializableLayer for more info.
|
||||
func (eth *Ethernet) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
|
||||
if len(eth.DstMAC) != 6 {
|
||||
return fmt.Errorf("invalid dst MAC: %v", eth.DstMAC)
|
||||
}
|
||||
if len(eth.SrcMAC) != 6 {
|
||||
return fmt.Errorf("invalid src MAC: %v", eth.SrcMAC)
|
||||
}
|
||||
payload := b.Bytes()
|
||||
bytes, err := b.PrependBytes(14)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
copy(bytes, eth.DstMAC)
|
||||
copy(bytes[6:], eth.SrcMAC)
|
||||
if eth.Length != 0 || eth.EthernetType == EthernetTypeLLC {
|
||||
if opts.FixLengths {
|
||||
eth.Length = uint16(len(payload))
|
||||
}
|
||||
if eth.EthernetType != EthernetTypeLLC {
|
||||
return fmt.Errorf("ethernet type %v not compatible with length value %v", eth.EthernetType, eth.Length)
|
||||
} else if eth.Length > 0x0600 {
|
||||
return fmt.Errorf("invalid ethernet length %v", eth.Length)
|
||||
}
|
||||
binary.BigEndian.PutUint16(bytes[12:], eth.Length)
|
||||
} else {
|
||||
binary.BigEndian.PutUint16(bytes[12:], uint16(eth.EthernetType))
|
||||
}
|
||||
length := len(b.Bytes())
|
||||
if length < 60 {
|
||||
// Pad out to 60 bytes.
|
||||
padding, err := b.AppendBytes(60 - length)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
copy(padding, lotsOfZeros[:])
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (eth *Ethernet) CanDecode() gopacket.LayerClass {
|
||||
return LayerTypeEthernet
|
||||
}
|
||||
|
||||
func (eth *Ethernet) NextLayerType() gopacket.LayerType {
|
||||
return eth.EthernetType.LayerType()
|
||||
}
|
||||
|
||||
func decodeEthernet(data []byte, p gopacket.PacketBuilder) error {
|
||||
eth := &Ethernet{}
|
||||
err := eth.DecodeFromBytes(data, p)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
p.AddLayer(eth)
|
||||
p.SetLinkLayer(eth)
|
||||
return p.NextDecoder(eth.EthernetType)
|
||||
}
|
||||
+41
@@ -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)
|
||||
}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
#!/bin/bash
|
||||
|
||||
for i in *.go; do golint $i | grep -q . || echo $i; done > .linted
|
||||
+110
@@ -0,0 +1,110 @@
|
||||
// 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) (*GeneveOption, uint8) {
|
||||
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
|
||||
|
||||
opt.Data = make([]byte, opt.Length-4)
|
||||
copy(opt.Data, data[4:opt.Length])
|
||||
|
||||
return opt, opt.Length
|
||||
}
|
||||
|
||||
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 := decodeGeneveOption(data[offset:], gn)
|
||||
gn.Options = append(gn.Options, opt)
|
||||
|
||||
length -= int32(len)
|
||||
offset += len
|
||||
}
|
||||
|
||||
gn.BaseLayer = BaseLayer{data[:offset], data[offset:]}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (gn *Geneve) NextLayerType() gopacket.LayerType {
|
||||
return gn.Protocol.LayerType()
|
||||
}
|
||||
|
||||
func decodeGeneve(data []byte, p gopacket.PacketBuilder) error {
|
||||
gn := &Geneve{}
|
||||
return decodingLayerDecoder(gn, data, p)
|
||||
}
|
||||
+200
@@ -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)
|
||||
}
|
||||
+181
@@ -0,0 +1,181 @@
|
||||
// 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 {
|
||||
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())
|
||||
}
|
||||
+11351
File diff suppressed because it is too large
Load Diff
+267
@@ -0,0 +1,267 @@
|
||||
// Copyright 2012 Google, Inc. All rights reserved.
|
||||
// Copyright 2009-2011 Andreas Krennmair. All rights reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the LICENSE file in the root of the source
|
||||
// tree.
|
||||
|
||||
package layers
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
|
||||
"github.com/google/gopacket"
|
||||
)
|
||||
|
||||
const (
|
||||
ICMPv4TypeEchoReply = 0
|
||||
ICMPv4TypeDestinationUnreachable = 3
|
||||
ICMPv4TypeSourceQuench = 4
|
||||
ICMPv4TypeRedirect = 5
|
||||
ICMPv4TypeEchoRequest = 8
|
||||
ICMPv4TypeRouterAdvertisement = 9
|
||||
ICMPv4TypeRouterSolicitation = 10
|
||||
ICMPv4TypeTimeExceeded = 11
|
||||
ICMPv4TypeParameterProblem = 12
|
||||
ICMPv4TypeTimestampRequest = 13
|
||||
ICMPv4TypeTimestampReply = 14
|
||||
ICMPv4TypeInfoRequest = 15
|
||||
ICMPv4TypeInfoReply = 16
|
||||
ICMPv4TypeAddressMaskRequest = 17
|
||||
ICMPv4TypeAddressMaskReply = 18
|
||||
)
|
||||
|
||||
const (
|
||||
// DestinationUnreachable
|
||||
ICMPv4CodeNet = 0
|
||||
ICMPv4CodeHost = 1
|
||||
ICMPv4CodeProtocol = 2
|
||||
ICMPv4CodePort = 3
|
||||
ICMPv4CodeFragmentationNeeded = 4
|
||||
ICMPv4CodeSourceRoutingFailed = 5
|
||||
ICMPv4CodeNetUnknown = 6
|
||||
ICMPv4CodeHostUnknown = 7
|
||||
ICMPv4CodeSourceIsolated = 8
|
||||
ICMPv4CodeNetAdminProhibited = 9
|
||||
ICMPv4CodeHostAdminProhibited = 10
|
||||
ICMPv4CodeNetTOS = 11
|
||||
ICMPv4CodeHostTOS = 12
|
||||
ICMPv4CodeCommAdminProhibited = 13
|
||||
ICMPv4CodeHostPrecedence = 14
|
||||
ICMPv4CodePrecedenceCutoff = 15
|
||||
|
||||
// TimeExceeded
|
||||
ICMPv4CodeTTLExceeded = 0
|
||||
ICMPv4CodeFragmentReassemblyTimeExceeded = 1
|
||||
|
||||
// ParameterProblem
|
||||
ICMPv4CodePointerIndicatesError = 0
|
||||
ICMPv4CodeMissingOption = 1
|
||||
ICMPv4CodeBadLength = 2
|
||||
|
||||
// Redirect
|
||||
// ICMPv4CodeNet = same as for DestinationUnreachable
|
||||
// ICMPv4CodeHost = same as for DestinationUnreachable
|
||||
ICMPv4CodeTOSNet = 2
|
||||
ICMPv4CodeTOSHost = 3
|
||||
)
|
||||
|
||||
type icmpv4TypeCodeInfoStruct struct {
|
||||
typeStr string
|
||||
codeStr *map[uint8]string
|
||||
}
|
||||
|
||||
var (
|
||||
icmpv4TypeCodeInfo = map[uint8]icmpv4TypeCodeInfoStruct{
|
||||
ICMPv4TypeDestinationUnreachable: icmpv4TypeCodeInfoStruct{
|
||||
"DestinationUnreachable", &map[uint8]string{
|
||||
ICMPv4CodeNet: "Net",
|
||||
ICMPv4CodeHost: "Host",
|
||||
ICMPv4CodeProtocol: "Protocol",
|
||||
ICMPv4CodePort: "Port",
|
||||
ICMPv4CodeFragmentationNeeded: "FragmentationNeeded",
|
||||
ICMPv4CodeSourceRoutingFailed: "SourceRoutingFailed",
|
||||
ICMPv4CodeNetUnknown: "NetUnknown",
|
||||
ICMPv4CodeHostUnknown: "HostUnknown",
|
||||
ICMPv4CodeSourceIsolated: "SourceIsolated",
|
||||
ICMPv4CodeNetAdminProhibited: "NetAdminProhibited",
|
||||
ICMPv4CodeHostAdminProhibited: "HostAdminProhibited",
|
||||
ICMPv4CodeNetTOS: "NetTOS",
|
||||
ICMPv4CodeHostTOS: "HostTOS",
|
||||
ICMPv4CodeCommAdminProhibited: "CommAdminProhibited",
|
||||
ICMPv4CodeHostPrecedence: "HostPrecedence",
|
||||
ICMPv4CodePrecedenceCutoff: "PrecedenceCutoff",
|
||||
},
|
||||
},
|
||||
ICMPv4TypeTimeExceeded: icmpv4TypeCodeInfoStruct{
|
||||
"TimeExceeded", &map[uint8]string{
|
||||
ICMPv4CodeTTLExceeded: "TTLExceeded",
|
||||
ICMPv4CodeFragmentReassemblyTimeExceeded: "FragmentReassemblyTimeExceeded",
|
||||
},
|
||||
},
|
||||
ICMPv4TypeParameterProblem: icmpv4TypeCodeInfoStruct{
|
||||
"ParameterProblem", &map[uint8]string{
|
||||
ICMPv4CodePointerIndicatesError: "PointerIndicatesError",
|
||||
ICMPv4CodeMissingOption: "MissingOption",
|
||||
ICMPv4CodeBadLength: "BadLength",
|
||||
},
|
||||
},
|
||||
ICMPv4TypeSourceQuench: icmpv4TypeCodeInfoStruct{
|
||||
"SourceQuench", nil,
|
||||
},
|
||||
ICMPv4TypeRedirect: icmpv4TypeCodeInfoStruct{
|
||||
"Redirect", &map[uint8]string{
|
||||
ICMPv4CodeNet: "Net",
|
||||
ICMPv4CodeHost: "Host",
|
||||
ICMPv4CodeTOSNet: "TOS+Net",
|
||||
ICMPv4CodeTOSHost: "TOS+Host",
|
||||
},
|
||||
},
|
||||
ICMPv4TypeEchoRequest: icmpv4TypeCodeInfoStruct{
|
||||
"EchoRequest", nil,
|
||||
},
|
||||
ICMPv4TypeEchoReply: icmpv4TypeCodeInfoStruct{
|
||||
"EchoReply", nil,
|
||||
},
|
||||
ICMPv4TypeTimestampRequest: icmpv4TypeCodeInfoStruct{
|
||||
"TimestampRequest", nil,
|
||||
},
|
||||
ICMPv4TypeTimestampReply: icmpv4TypeCodeInfoStruct{
|
||||
"TimestampReply", nil,
|
||||
},
|
||||
ICMPv4TypeInfoRequest: icmpv4TypeCodeInfoStruct{
|
||||
"InfoRequest", nil,
|
||||
},
|
||||
ICMPv4TypeInfoReply: icmpv4TypeCodeInfoStruct{
|
||||
"InfoReply", nil,
|
||||
},
|
||||
ICMPv4TypeRouterSolicitation: icmpv4TypeCodeInfoStruct{
|
||||
"RouterSolicitation", nil,
|
||||
},
|
||||
ICMPv4TypeRouterAdvertisement: icmpv4TypeCodeInfoStruct{
|
||||
"RouterAdvertisement", nil,
|
||||
},
|
||||
ICMPv4TypeAddressMaskRequest: icmpv4TypeCodeInfoStruct{
|
||||
"AddressMaskRequest", nil,
|
||||
},
|
||||
ICMPv4TypeAddressMaskReply: icmpv4TypeCodeInfoStruct{
|
||||
"AddressMaskReply", nil,
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
type ICMPv4TypeCode uint16
|
||||
|
||||
// Type returns the ICMPv4 type field.
|
||||
func (a ICMPv4TypeCode) Type() uint8 {
|
||||
return uint8(a >> 8)
|
||||
}
|
||||
|
||||
// Code returns the ICMPv4 code field.
|
||||
func (a ICMPv4TypeCode) Code() uint8 {
|
||||
return uint8(a)
|
||||
}
|
||||
|
||||
func (a ICMPv4TypeCode) String() string {
|
||||
t, c := a.Type(), a.Code()
|
||||
strInfo, ok := icmpv4TypeCodeInfo[t]
|
||||
if !ok {
|
||||
// Unknown ICMPv4 type field
|
||||
return fmt.Sprintf("%d(%d)", t, c)
|
||||
}
|
||||
typeStr := strInfo.typeStr
|
||||
if strInfo.codeStr == nil && c == 0 {
|
||||
// The ICMPv4 type does not make use of the code field
|
||||
return fmt.Sprintf("%s", strInfo.typeStr)
|
||||
}
|
||||
if strInfo.codeStr == nil && c != 0 {
|
||||
// The ICMPv4 type does not make use of the code field, but it is present anyway
|
||||
return fmt.Sprintf("%s(Code: %d)", typeStr, c)
|
||||
}
|
||||
codeStr, ok := (*strInfo.codeStr)[c]
|
||||
if !ok {
|
||||
// We don't know this ICMPv4 code; print the numerical value
|
||||
return fmt.Sprintf("%s(Code: %d)", typeStr, c)
|
||||
}
|
||||
return fmt.Sprintf("%s(%s)", typeStr, codeStr)
|
||||
}
|
||||
|
||||
func (a ICMPv4TypeCode) GoString() string {
|
||||
t := reflect.TypeOf(a)
|
||||
return fmt.Sprintf("%s(%d, %d)", t.String(), a.Type(), a.Code())
|
||||
}
|
||||
|
||||
// SerializeTo writes the ICMPv4TypeCode value to the 'bytes' buffer.
|
||||
func (a ICMPv4TypeCode) SerializeTo(bytes []byte) {
|
||||
binary.BigEndian.PutUint16(bytes, uint16(a))
|
||||
}
|
||||
|
||||
// CreateICMPv4TypeCode is a convenience function to create an ICMPv4TypeCode
|
||||
// gopacket type from the ICMPv4 type and code values.
|
||||
func CreateICMPv4TypeCode(typ uint8, code uint8) ICMPv4TypeCode {
|
||||
return ICMPv4TypeCode(binary.BigEndian.Uint16([]byte{typ, code}))
|
||||
}
|
||||
|
||||
// ICMPv4 is the layer for IPv4 ICMP packet data.
|
||||
type ICMPv4 struct {
|
||||
BaseLayer
|
||||
TypeCode ICMPv4TypeCode
|
||||
Checksum uint16
|
||||
Id uint16
|
||||
Seq uint16
|
||||
}
|
||||
|
||||
// LayerType returns LayerTypeICMPv4.
|
||||
func (i *ICMPv4) LayerType() gopacket.LayerType { return LayerTypeICMPv4 }
|
||||
|
||||
// DecodeFromBytes decodes the given bytes into this layer.
|
||||
func (i *ICMPv4) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
|
||||
if len(data) < 8 {
|
||||
df.SetTruncated()
|
||||
return errors.New("ICMP layer less then 8 bytes for ICMPv4 packet")
|
||||
}
|
||||
i.TypeCode = CreateICMPv4TypeCode(data[0], data[1])
|
||||
i.Checksum = binary.BigEndian.Uint16(data[2:4])
|
||||
i.Id = binary.BigEndian.Uint16(data[4:6])
|
||||
i.Seq = binary.BigEndian.Uint16(data[6:8])
|
||||
i.BaseLayer = BaseLayer{data[:8], data[8:]}
|
||||
return nil
|
||||
}
|
||||
|
||||
// SerializeTo writes the serialized form of this layer into the
|
||||
// SerializationBuffer, implementing gopacket.SerializableLayer.
|
||||
// See the docs for gopacket.SerializableLayer for more info.
|
||||
func (i *ICMPv4) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
|
||||
bytes, err := b.PrependBytes(8)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
i.TypeCode.SerializeTo(bytes)
|
||||
binary.BigEndian.PutUint16(bytes[4:], i.Id)
|
||||
binary.BigEndian.PutUint16(bytes[6:], i.Seq)
|
||||
if opts.ComputeChecksums {
|
||||
bytes[2] = 0
|
||||
bytes[3] = 0
|
||||
i.Checksum = tcpipChecksum(b.Bytes(), 0)
|
||||
}
|
||||
binary.BigEndian.PutUint16(bytes[2:], i.Checksum)
|
||||
return nil
|
||||
}
|
||||
|
||||
// CanDecode returns the set of layer types that this DecodingLayer can decode.
|
||||
func (i *ICMPv4) CanDecode() gopacket.LayerClass {
|
||||
return LayerTypeICMPv4
|
||||
}
|
||||
|
||||
// NextLayerType returns the layer type contained by this DecodingLayer.
|
||||
func (i *ICMPv4) NextLayerType() gopacket.LayerType {
|
||||
return gopacket.LayerTypePayload
|
||||
}
|
||||
|
||||
func decodeICMPv4(data []byte, p gopacket.PacketBuilder) error {
|
||||
i := &ICMPv4{}
|
||||
return decodingLayerDecoder(i, data, p)
|
||||
}
|
||||
+266
@@ -0,0 +1,266 @@
|
||||
// Copyright 2012 Google, Inc. All rights reserved.
|
||||
// Copyright 2009-2011 Andreas Krennmair. All rights reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the LICENSE file in the root of the source
|
||||
// tree.
|
||||
|
||||
package layers
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
|
||||
"github.com/google/gopacket"
|
||||
)
|
||||
|
||||
const (
|
||||
// The following are from RFC 4443
|
||||
ICMPv6TypeDestinationUnreachable = 1
|
||||
ICMPv6TypePacketTooBig = 2
|
||||
ICMPv6TypeTimeExceeded = 3
|
||||
ICMPv6TypeParameterProblem = 4
|
||||
ICMPv6TypeEchoRequest = 128
|
||||
ICMPv6TypeEchoReply = 129
|
||||
|
||||
// The following are from RFC 4861
|
||||
ICMPv6TypeRouterSolicitation = 133
|
||||
ICMPv6TypeRouterAdvertisement = 134
|
||||
ICMPv6TypeNeighborSolicitation = 135
|
||||
ICMPv6TypeNeighborAdvertisement = 136
|
||||
ICMPv6TypeRedirect = 137
|
||||
|
||||
// The following are from RFC 2710
|
||||
ICMPv6TypeMLDv1MulticastListenerQueryMessage = 130
|
||||
ICMPv6TypeMLDv1MulticastListenerReportMessage = 131
|
||||
ICMPv6TypeMLDv1MulticastListenerDoneMessage = 132
|
||||
|
||||
// The following are from RFC 3810
|
||||
ICMPv6TypeMLDv2MulticastListenerReportMessageV2 = 143
|
||||
)
|
||||
|
||||
const (
|
||||
// DestinationUnreachable
|
||||
ICMPv6CodeNoRouteToDst = 0
|
||||
ICMPv6CodeAdminProhibited = 1
|
||||
ICMPv6CodeBeyondScopeOfSrc = 2
|
||||
ICMPv6CodeAddressUnreachable = 3
|
||||
ICMPv6CodePortUnreachable = 4
|
||||
ICMPv6CodeSrcAddressFailedPolicy = 5
|
||||
ICMPv6CodeRejectRouteToDst = 6
|
||||
|
||||
// TimeExceeded
|
||||
ICMPv6CodeHopLimitExceeded = 0
|
||||
ICMPv6CodeFragmentReassemblyTimeExceeded = 1
|
||||
|
||||
// ParameterProblem
|
||||
ICMPv6CodeErroneousHeaderField = 0
|
||||
ICMPv6CodeUnrecognizedNextHeader = 1
|
||||
ICMPv6CodeUnrecognizedIPv6Option = 2
|
||||
)
|
||||
|
||||
type icmpv6TypeCodeInfoStruct struct {
|
||||
typeStr string
|
||||
codeStr *map[uint8]string
|
||||
}
|
||||
|
||||
var (
|
||||
icmpv6TypeCodeInfo = map[uint8]icmpv6TypeCodeInfoStruct{
|
||||
ICMPv6TypeDestinationUnreachable: icmpv6TypeCodeInfoStruct{
|
||||
"DestinationUnreachable", &map[uint8]string{
|
||||
ICMPv6CodeNoRouteToDst: "NoRouteToDst",
|
||||
ICMPv6CodeAdminProhibited: "AdminProhibited",
|
||||
ICMPv6CodeBeyondScopeOfSrc: "BeyondScopeOfSrc",
|
||||
ICMPv6CodeAddressUnreachable: "AddressUnreachable",
|
||||
ICMPv6CodePortUnreachable: "PortUnreachable",
|
||||
ICMPv6CodeSrcAddressFailedPolicy: "SrcAddressFailedPolicy",
|
||||
ICMPv6CodeRejectRouteToDst: "RejectRouteToDst",
|
||||
},
|
||||
},
|
||||
ICMPv6TypePacketTooBig: icmpv6TypeCodeInfoStruct{
|
||||
"PacketTooBig", nil,
|
||||
},
|
||||
ICMPv6TypeTimeExceeded: icmpv6TypeCodeInfoStruct{
|
||||
"TimeExceeded", &map[uint8]string{
|
||||
ICMPv6CodeHopLimitExceeded: "HopLimitExceeded",
|
||||
ICMPv6CodeFragmentReassemblyTimeExceeded: "FragmentReassemblyTimeExceeded",
|
||||
},
|
||||
},
|
||||
ICMPv6TypeParameterProblem: icmpv6TypeCodeInfoStruct{
|
||||
"ParameterProblem", &map[uint8]string{
|
||||
ICMPv6CodeErroneousHeaderField: "ErroneousHeaderField",
|
||||
ICMPv6CodeUnrecognizedNextHeader: "UnrecognizedNextHeader",
|
||||
ICMPv6CodeUnrecognizedIPv6Option: "UnrecognizedIPv6Option",
|
||||
},
|
||||
},
|
||||
ICMPv6TypeEchoRequest: icmpv6TypeCodeInfoStruct{
|
||||
"EchoRequest", nil,
|
||||
},
|
||||
ICMPv6TypeEchoReply: icmpv6TypeCodeInfoStruct{
|
||||
"EchoReply", nil,
|
||||
},
|
||||
ICMPv6TypeRouterSolicitation: icmpv6TypeCodeInfoStruct{
|
||||
"RouterSolicitation", nil,
|
||||
},
|
||||
ICMPv6TypeRouterAdvertisement: icmpv6TypeCodeInfoStruct{
|
||||
"RouterAdvertisement", nil,
|
||||
},
|
||||
ICMPv6TypeNeighborSolicitation: icmpv6TypeCodeInfoStruct{
|
||||
"NeighborSolicitation", nil,
|
||||
},
|
||||
ICMPv6TypeNeighborAdvertisement: icmpv6TypeCodeInfoStruct{
|
||||
"NeighborAdvertisement", nil,
|
||||
},
|
||||
ICMPv6TypeRedirect: icmpv6TypeCodeInfoStruct{
|
||||
"Redirect", nil,
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
type ICMPv6TypeCode uint16
|
||||
|
||||
// Type returns the ICMPv6 type field.
|
||||
func (a ICMPv6TypeCode) Type() uint8 {
|
||||
return uint8(a >> 8)
|
||||
}
|
||||
|
||||
// Code returns the ICMPv6 code field.
|
||||
func (a ICMPv6TypeCode) Code() uint8 {
|
||||
return uint8(a)
|
||||
}
|
||||
|
||||
func (a ICMPv6TypeCode) String() string {
|
||||
t, c := a.Type(), a.Code()
|
||||
strInfo, ok := icmpv6TypeCodeInfo[t]
|
||||
if !ok {
|
||||
// Unknown ICMPv6 type field
|
||||
return fmt.Sprintf("%d(%d)", t, c)
|
||||
}
|
||||
typeStr := strInfo.typeStr
|
||||
if strInfo.codeStr == nil && c == 0 {
|
||||
// The ICMPv6 type does not make use of the code field
|
||||
return fmt.Sprintf("%s", strInfo.typeStr)
|
||||
}
|
||||
if strInfo.codeStr == nil && c != 0 {
|
||||
// The ICMPv6 type does not make use of the code field, but it is present anyway
|
||||
return fmt.Sprintf("%s(Code: %d)", typeStr, c)
|
||||
}
|
||||
codeStr, ok := (*strInfo.codeStr)[c]
|
||||
if !ok {
|
||||
// We don't know this ICMPv6 code; print the numerical value
|
||||
return fmt.Sprintf("%s(Code: %d)", typeStr, c)
|
||||
}
|
||||
return fmt.Sprintf("%s(%s)", typeStr, codeStr)
|
||||
}
|
||||
|
||||
func (a ICMPv6TypeCode) GoString() string {
|
||||
t := reflect.TypeOf(a)
|
||||
return fmt.Sprintf("%s(%d, %d)", t.String(), a.Type(), a.Code())
|
||||
}
|
||||
|
||||
// SerializeTo writes the ICMPv6TypeCode value to the 'bytes' buffer.
|
||||
func (a ICMPv6TypeCode) SerializeTo(bytes []byte) {
|
||||
binary.BigEndian.PutUint16(bytes, uint16(a))
|
||||
}
|
||||
|
||||
// CreateICMPv6TypeCode is a convenience function to create an ICMPv6TypeCode
|
||||
// gopacket type from the ICMPv6 type and code values.
|
||||
func CreateICMPv6TypeCode(typ uint8, code uint8) ICMPv6TypeCode {
|
||||
return ICMPv6TypeCode(binary.BigEndian.Uint16([]byte{typ, code}))
|
||||
}
|
||||
|
||||
// ICMPv6 is the layer for IPv6 ICMP packet data
|
||||
type ICMPv6 struct {
|
||||
BaseLayer
|
||||
TypeCode ICMPv6TypeCode
|
||||
Checksum uint16
|
||||
// TypeBytes is deprecated and always nil. See the different ICMPv6 message types
|
||||
// instead (e.g. ICMPv6TypeRouterSolicitation).
|
||||
TypeBytes []byte
|
||||
tcpipchecksum
|
||||
}
|
||||
|
||||
// LayerType returns LayerTypeICMPv6.
|
||||
func (i *ICMPv6) LayerType() gopacket.LayerType { return LayerTypeICMPv6 }
|
||||
|
||||
// DecodeFromBytes decodes the given bytes into this layer.
|
||||
func (i *ICMPv6) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
|
||||
if len(data) < 4 {
|
||||
df.SetTruncated()
|
||||
return errors.New("ICMP layer less then 4 bytes for ICMPv6 packet")
|
||||
}
|
||||
i.TypeCode = CreateICMPv6TypeCode(data[0], data[1])
|
||||
i.Checksum = binary.BigEndian.Uint16(data[2:4])
|
||||
i.BaseLayer = BaseLayer{data[:4], data[4:]}
|
||||
return nil
|
||||
}
|
||||
|
||||
// SerializeTo writes the serialized form of this layer into the
|
||||
// SerializationBuffer, implementing gopacket.SerializableLayer.
|
||||
// See the docs for gopacket.SerializableLayer for more info.
|
||||
func (i *ICMPv6) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
|
||||
bytes, err := b.PrependBytes(4)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
i.TypeCode.SerializeTo(bytes)
|
||||
|
||||
if opts.ComputeChecksums {
|
||||
bytes[2] = 0
|
||||
bytes[3] = 0
|
||||
csum, err := i.computeChecksum(b.Bytes(), IPProtocolICMPv6)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
i.Checksum = csum
|
||||
}
|
||||
binary.BigEndian.PutUint16(bytes[2:], i.Checksum)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// CanDecode returns the set of layer types that this DecodingLayer can decode.
|
||||
func (i *ICMPv6) CanDecode() gopacket.LayerClass {
|
||||
return LayerTypeICMPv6
|
||||
}
|
||||
|
||||
// NextLayerType returns the layer type contained by this DecodingLayer.
|
||||
func (i *ICMPv6) NextLayerType() gopacket.LayerType {
|
||||
switch i.TypeCode.Type() {
|
||||
case ICMPv6TypeEchoRequest:
|
||||
return LayerTypeICMPv6Echo
|
||||
case ICMPv6TypeEchoReply:
|
||||
return LayerTypeICMPv6Echo
|
||||
case ICMPv6TypeRouterSolicitation:
|
||||
return LayerTypeICMPv6RouterSolicitation
|
||||
case ICMPv6TypeRouterAdvertisement:
|
||||
return LayerTypeICMPv6RouterAdvertisement
|
||||
case ICMPv6TypeNeighborSolicitation:
|
||||
return LayerTypeICMPv6NeighborSolicitation
|
||||
case ICMPv6TypeNeighborAdvertisement:
|
||||
return LayerTypeICMPv6NeighborAdvertisement
|
||||
case ICMPv6TypeRedirect:
|
||||
return LayerTypeICMPv6Redirect
|
||||
case ICMPv6TypeMLDv1MulticastListenerQueryMessage: // Same Code for MLDv1 Query and MLDv2 Query
|
||||
if len(i.Payload) > 20 { // Only payload size differs
|
||||
return LayerTypeMLDv2MulticastListenerQuery
|
||||
} else {
|
||||
return LayerTypeMLDv1MulticastListenerQuery
|
||||
}
|
||||
case ICMPv6TypeMLDv1MulticastListenerDoneMessage:
|
||||
return LayerTypeMLDv1MulticastListenerDone
|
||||
case ICMPv6TypeMLDv1MulticastListenerReportMessage:
|
||||
return LayerTypeMLDv1MulticastListenerReport
|
||||
case ICMPv6TypeMLDv2MulticastListenerReportMessageV2:
|
||||
return LayerTypeMLDv2MulticastListenerReport
|
||||
}
|
||||
|
||||
return gopacket.LayerTypePayload
|
||||
}
|
||||
|
||||
func decodeICMPv6(data []byte, p gopacket.PacketBuilder) error {
|
||||
i := &ICMPv6{}
|
||||
return decodingLayerDecoder(i, data, p)
|
||||
}
|
||||
+578
@@ -0,0 +1,578 @@
|
||||
// Copyright 2012 Google, Inc. All rights reserved.
|
||||
// Copyright 2009-2011 Andreas Krennmair. All rights reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the LICENSE file in the root of the source
|
||||
// tree.
|
||||
|
||||
package layers
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/google/gopacket"
|
||||
)
|
||||
|
||||
// Based on RFC 4861
|
||||
|
||||
// ICMPv6Opt indicate how to decode the data associated with each ICMPv6Option.
|
||||
type ICMPv6Opt uint8
|
||||
|
||||
const (
|
||||
_ ICMPv6Opt = iota
|
||||
|
||||
// ICMPv6OptSourceAddress contains the link-layer address of the sender of
|
||||
// the packet. It is used in the Neighbor Solicitation, Router
|
||||
// Solicitation, and Router Advertisement packets. Must be ignored for other
|
||||
// Neighbor discovery messages.
|
||||
ICMPv6OptSourceAddress
|
||||
|
||||
// ICMPv6OptTargetAddress contains the link-layer address of the target. It
|
||||
// is used in Neighbor Advertisement and Redirect packets. Must be ignored
|
||||
// for other Neighbor discovery messages.
|
||||
ICMPv6OptTargetAddress
|
||||
|
||||
// ICMPv6OptPrefixInfo provides hosts with on-link prefixes and prefixes
|
||||
// for Address Autoconfiguration. The Prefix Information option appears in
|
||||
// Router Advertisement packets and MUST be silently ignored for other
|
||||
// messages.
|
||||
ICMPv6OptPrefixInfo
|
||||
|
||||
// ICMPv6OptRedirectedHeader is used in Redirect messages and contains all
|
||||
// or part of the packet that is being redirected.
|
||||
ICMPv6OptRedirectedHeader
|
||||
|
||||
// ICMPv6OptMTU is used in Router Advertisement messages to ensure that all
|
||||
// nodes on a link use the same MTU value in those cases where the link MTU
|
||||
// is not well known. This option MUST be silently ignored for other
|
||||
// Neighbor Discovery messages.
|
||||
ICMPv6OptMTU
|
||||
)
|
||||
|
||||
// ICMPv6Echo represents the structure of a ping.
|
||||
type ICMPv6Echo struct {
|
||||
BaseLayer
|
||||
Identifier uint16
|
||||
SeqNumber uint16
|
||||
}
|
||||
|
||||
// ICMPv6RouterSolicitation is sent by hosts to find routers.
|
||||
type ICMPv6RouterSolicitation struct {
|
||||
BaseLayer
|
||||
Options ICMPv6Options
|
||||
}
|
||||
|
||||
// ICMPv6RouterAdvertisement is sent by routers in response to Solicitation.
|
||||
type ICMPv6RouterAdvertisement struct {
|
||||
BaseLayer
|
||||
HopLimit uint8
|
||||
Flags uint8
|
||||
RouterLifetime uint16
|
||||
ReachableTime uint32
|
||||
RetransTimer uint32
|
||||
Options ICMPv6Options
|
||||
}
|
||||
|
||||
// ICMPv6NeighborSolicitation is sent to request the link-layer address of a
|
||||
// target node.
|
||||
type ICMPv6NeighborSolicitation struct {
|
||||
BaseLayer
|
||||
TargetAddress net.IP
|
||||
Options ICMPv6Options
|
||||
}
|
||||
|
||||
// ICMPv6NeighborAdvertisement is sent by nodes in response to Solicitation.
|
||||
type ICMPv6NeighborAdvertisement struct {
|
||||
BaseLayer
|
||||
Flags uint8
|
||||
TargetAddress net.IP
|
||||
Options ICMPv6Options
|
||||
}
|
||||
|
||||
// ICMPv6Redirect is sent by routers to inform hosts of a better first-hop node
|
||||
// on the path to a destination.
|
||||
type ICMPv6Redirect struct {
|
||||
BaseLayer
|
||||
TargetAddress net.IP
|
||||
DestinationAddress net.IP
|
||||
Options ICMPv6Options
|
||||
}
|
||||
|
||||
// ICMPv6Option contains the type and data for a single option.
|
||||
type ICMPv6Option struct {
|
||||
Type ICMPv6Opt
|
||||
Data []byte
|
||||
}
|
||||
|
||||
// ICMPv6Options is a slice of ICMPv6Option.
|
||||
type ICMPv6Options []ICMPv6Option
|
||||
|
||||
func (i ICMPv6Opt) String() string {
|
||||
switch i {
|
||||
case ICMPv6OptSourceAddress:
|
||||
return "SourceAddress"
|
||||
case ICMPv6OptTargetAddress:
|
||||
return "TargetAddress"
|
||||
case ICMPv6OptPrefixInfo:
|
||||
return "PrefixInfo"
|
||||
case ICMPv6OptRedirectedHeader:
|
||||
return "RedirectedHeader"
|
||||
case ICMPv6OptMTU:
|
||||
return "MTU"
|
||||
default:
|
||||
return fmt.Sprintf("Unknown(%d)", i)
|
||||
}
|
||||
}
|
||||
|
||||
// CanDecode returns the set of layer types that this DecodingLayer can decode.
|
||||
func (i *ICMPv6Echo) CanDecode() gopacket.LayerClass {
|
||||
return LayerTypeICMPv6Echo
|
||||
}
|
||||
|
||||
// LayerType returns LayerTypeICMPv6Echo.
|
||||
func (i *ICMPv6Echo) LayerType() gopacket.LayerType {
|
||||
return LayerTypeICMPv6Echo
|
||||
}
|
||||
|
||||
// NextLayerType returns the layer type contained by this DecodingLayer.
|
||||
func (i *ICMPv6Echo) NextLayerType() gopacket.LayerType {
|
||||
return gopacket.LayerTypePayload
|
||||
}
|
||||
|
||||
// DecodeFromBytes decodes the given bytes into this layer.
|
||||
func (i *ICMPv6Echo) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
|
||||
if len(data) < 4 {
|
||||
df.SetTruncated()
|
||||
return errors.New("ICMP layer less then 4 bytes for ICMPv6 Echo")
|
||||
}
|
||||
i.Identifier = binary.BigEndian.Uint16(data[0:2])
|
||||
i.SeqNumber = binary.BigEndian.Uint16(data[2:4])
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// SerializeTo writes the serialized form of this layer into the
|
||||
// SerializationBuffer, implementing gopacket.SerializableLayer.
|
||||
// See the docs for gopacket.SerializableLayer for more info.
|
||||
func (i *ICMPv6Echo) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
|
||||
buf, err := b.PrependBytes(4)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
binary.BigEndian.PutUint16(buf, i.Identifier)
|
||||
binary.BigEndian.PutUint16(buf[2:], i.SeqNumber)
|
||||
return nil
|
||||
}
|
||||
|
||||
// LayerType returns LayerTypeICMPv6.
|
||||
func (i *ICMPv6RouterSolicitation) LayerType() gopacket.LayerType {
|
||||
return LayerTypeICMPv6RouterSolicitation
|
||||
}
|
||||
|
||||
// NextLayerType returns the layer type contained by this DecodingLayer.
|
||||
func (i *ICMPv6RouterSolicitation) NextLayerType() gopacket.LayerType {
|
||||
return gopacket.LayerTypePayload
|
||||
}
|
||||
|
||||
// DecodeFromBytes decodes the given bytes into this layer.
|
||||
func (i *ICMPv6RouterSolicitation) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
|
||||
// first 4 bytes are reserved followed by options
|
||||
if len(data) < 4 {
|
||||
df.SetTruncated()
|
||||
return errors.New("ICMP layer less then 4 bytes for ICMPv6 router solicitation")
|
||||
}
|
||||
|
||||
// truncate old options
|
||||
i.Options = i.Options[:0]
|
||||
|
||||
return i.Options.DecodeFromBytes(data[4:], df)
|
||||
}
|
||||
|
||||
// SerializeTo writes the serialized form of this layer into the
|
||||
// SerializationBuffer, implementing gopacket.SerializableLayer.
|
||||
// See the docs for gopacket.SerializableLayer for more info.
|
||||
func (i *ICMPv6RouterSolicitation) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
|
||||
if err := i.Options.SerializeTo(b, opts); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
buf, err := b.PrependBytes(4)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
copy(buf, lotsOfZeros[:4])
|
||||
return nil
|
||||
}
|
||||
|
||||
// CanDecode returns the set of layer types that this DecodingLayer can decode.
|
||||
func (i *ICMPv6RouterSolicitation) CanDecode() gopacket.LayerClass {
|
||||
return LayerTypeICMPv6RouterSolicitation
|
||||
}
|
||||
|
||||
// LayerType returns LayerTypeICMPv6RouterAdvertisement.
|
||||
func (i *ICMPv6RouterAdvertisement) LayerType() gopacket.LayerType {
|
||||
return LayerTypeICMPv6RouterAdvertisement
|
||||
}
|
||||
|
||||
// NextLayerType returns the layer type contained by this DecodingLayer.
|
||||
func (i *ICMPv6RouterAdvertisement) NextLayerType() gopacket.LayerType {
|
||||
return gopacket.LayerTypePayload
|
||||
}
|
||||
|
||||
// DecodeFromBytes decodes the given bytes into this layer.
|
||||
func (i *ICMPv6RouterAdvertisement) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
|
||||
if len(data) < 12 {
|
||||
df.SetTruncated()
|
||||
return errors.New("ICMP layer less then 12 bytes for ICMPv6 router advertisement")
|
||||
}
|
||||
|
||||
i.HopLimit = uint8(data[0])
|
||||
// M, O bit followed by 6 reserved bits
|
||||
i.Flags = uint8(data[1])
|
||||
i.RouterLifetime = binary.BigEndian.Uint16(data[2:4])
|
||||
i.ReachableTime = binary.BigEndian.Uint32(data[4:8])
|
||||
i.RetransTimer = binary.BigEndian.Uint32(data[8:12])
|
||||
i.BaseLayer = BaseLayer{data, nil} // assume no payload
|
||||
|
||||
// truncate old options
|
||||
i.Options = i.Options[:0]
|
||||
|
||||
return i.Options.DecodeFromBytes(data[12:], df)
|
||||
}
|
||||
|
||||
// SerializeTo writes the serialized form of this layer into the
|
||||
// SerializationBuffer, implementing gopacket.SerializableLayer.
|
||||
// See the docs for gopacket.SerializableLayer for more info.
|
||||
func (i *ICMPv6RouterAdvertisement) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
|
||||
if err := i.Options.SerializeTo(b, opts); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
buf, err := b.PrependBytes(12)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
buf[0] = byte(i.HopLimit)
|
||||
buf[1] = byte(i.Flags)
|
||||
binary.BigEndian.PutUint16(buf[2:], i.RouterLifetime)
|
||||
binary.BigEndian.PutUint32(buf[4:], i.ReachableTime)
|
||||
binary.BigEndian.PutUint32(buf[8:], i.RetransTimer)
|
||||
return nil
|
||||
}
|
||||
|
||||
// CanDecode returns the set of layer types that this DecodingLayer can decode.
|
||||
func (i *ICMPv6RouterAdvertisement) CanDecode() gopacket.LayerClass {
|
||||
return LayerTypeICMPv6RouterAdvertisement
|
||||
}
|
||||
|
||||
// ManagedAddressConfig is true when addresses are available via DHCPv6. If
|
||||
// set, the OtherConfig flag is redundant.
|
||||
func (i *ICMPv6RouterAdvertisement) ManagedAddressConfig() bool {
|
||||
return i.Flags&0x80 != 0
|
||||
}
|
||||
|
||||
// OtherConfig is true when there is other configuration information available
|
||||
// via DHCPv6. For example, DNS-related information.
|
||||
func (i *ICMPv6RouterAdvertisement) OtherConfig() bool {
|
||||
return i.Flags&0x40 != 0
|
||||
}
|
||||
|
||||
// LayerType returns LayerTypeICMPv6NeighborSolicitation.
|
||||
func (i *ICMPv6NeighborSolicitation) LayerType() gopacket.LayerType {
|
||||
return LayerTypeICMPv6NeighborSolicitation
|
||||
}
|
||||
|
||||
// NextLayerType returns the layer type contained by this DecodingLayer.
|
||||
func (i *ICMPv6NeighborSolicitation) NextLayerType() gopacket.LayerType {
|
||||
return gopacket.LayerTypePayload
|
||||
}
|
||||
|
||||
// DecodeFromBytes decodes the given bytes into this layer.
|
||||
func (i *ICMPv6NeighborSolicitation) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
|
||||
if len(data) < 20 {
|
||||
df.SetTruncated()
|
||||
return errors.New("ICMP layer less then 20 bytes for ICMPv6 neighbor solicitation")
|
||||
}
|
||||
|
||||
i.TargetAddress = net.IP(data[4:20])
|
||||
i.BaseLayer = BaseLayer{data, nil} // assume no payload
|
||||
|
||||
// truncate old options
|
||||
i.Options = i.Options[:0]
|
||||
|
||||
return i.Options.DecodeFromBytes(data[20:], df)
|
||||
}
|
||||
|
||||
// SerializeTo writes the serialized form of this layer into the
|
||||
// SerializationBuffer, implementing gopacket.SerializableLayer.
|
||||
// See the docs for gopacket.SerializableLayer for more info.
|
||||
func (i *ICMPv6NeighborSolicitation) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
|
||||
if err := i.Options.SerializeTo(b, opts); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
buf, err := b.PrependBytes(20)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
copy(buf, lotsOfZeros[:4])
|
||||
copy(buf[4:], i.TargetAddress)
|
||||
return nil
|
||||
}
|
||||
|
||||
// CanDecode returns the set of layer types that this DecodingLayer can decode.
|
||||
func (i *ICMPv6NeighborSolicitation) CanDecode() gopacket.LayerClass {
|
||||
return LayerTypeICMPv6NeighborSolicitation
|
||||
}
|
||||
|
||||
// LayerType returns LayerTypeICMPv6NeighborAdvertisement.
|
||||
func (i *ICMPv6NeighborAdvertisement) LayerType() gopacket.LayerType {
|
||||
return LayerTypeICMPv6NeighborAdvertisement
|
||||
}
|
||||
|
||||
// NextLayerType returns the layer type contained by this DecodingLayer.
|
||||
func (i *ICMPv6NeighborAdvertisement) NextLayerType() gopacket.LayerType {
|
||||
return gopacket.LayerTypePayload
|
||||
}
|
||||
|
||||
// DecodeFromBytes decodes the given bytes into this layer.
|
||||
func (i *ICMPv6NeighborAdvertisement) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
|
||||
if len(data) < 20 {
|
||||
df.SetTruncated()
|
||||
return errors.New("ICMP layer less then 20 bytes for ICMPv6 neighbor advertisement")
|
||||
}
|
||||
|
||||
i.Flags = uint8(data[0])
|
||||
i.TargetAddress = net.IP(data[4:20])
|
||||
i.BaseLayer = BaseLayer{data, nil} // assume no payload
|
||||
|
||||
// truncate old options
|
||||
i.Options = i.Options[:0]
|
||||
|
||||
return i.Options.DecodeFromBytes(data[20:], df)
|
||||
}
|
||||
|
||||
// SerializeTo writes the serialized form of this layer into the
|
||||
// SerializationBuffer, implementing gopacket.SerializableLayer.
|
||||
// See the docs for gopacket.SerializableLayer for more info.
|
||||
func (i *ICMPv6NeighborAdvertisement) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
|
||||
if err := i.Options.SerializeTo(b, opts); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
buf, err := b.PrependBytes(20)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
buf[0] = byte(i.Flags)
|
||||
copy(buf[1:], lotsOfZeros[:3])
|
||||
copy(buf[4:], i.TargetAddress)
|
||||
return nil
|
||||
}
|
||||
|
||||
// CanDecode returns the set of layer types that this DecodingLayer can decode.
|
||||
func (i *ICMPv6NeighborAdvertisement) CanDecode() gopacket.LayerClass {
|
||||
return LayerTypeICMPv6NeighborAdvertisement
|
||||
}
|
||||
|
||||
// Router indicates whether the sender is a router or not.
|
||||
func (i *ICMPv6NeighborAdvertisement) Router() bool {
|
||||
return i.Flags&0x80 != 0
|
||||
}
|
||||
|
||||
// Solicited indicates whether the advertisement was solicited or not.
|
||||
func (i *ICMPv6NeighborAdvertisement) Solicited() bool {
|
||||
return i.Flags&0x40 != 0
|
||||
}
|
||||
|
||||
// Override indicates whether the advertisement should Override an existing
|
||||
// cache entry.
|
||||
func (i *ICMPv6NeighborAdvertisement) Override() bool {
|
||||
return i.Flags&0x20 != 0
|
||||
}
|
||||
|
||||
// LayerType returns LayerTypeICMPv6Redirect.
|
||||
func (i *ICMPv6Redirect) LayerType() gopacket.LayerType {
|
||||
return LayerTypeICMPv6Redirect
|
||||
}
|
||||
|
||||
// NextLayerType returns the layer type contained by this DecodingLayer.
|
||||
func (i *ICMPv6Redirect) NextLayerType() gopacket.LayerType {
|
||||
return gopacket.LayerTypePayload
|
||||
}
|
||||
|
||||
// DecodeFromBytes decodes the given bytes into this layer.
|
||||
func (i *ICMPv6Redirect) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
|
||||
if len(data) < 36 {
|
||||
df.SetTruncated()
|
||||
return errors.New("ICMP layer less then 36 bytes for ICMPv6 redirect")
|
||||
}
|
||||
|
||||
i.TargetAddress = net.IP(data[4:20])
|
||||
i.DestinationAddress = net.IP(data[20:36])
|
||||
i.BaseLayer = BaseLayer{data, nil} // assume no payload
|
||||
|
||||
// truncate old options
|
||||
i.Options = i.Options[:0]
|
||||
|
||||
return i.Options.DecodeFromBytes(data[36:], df)
|
||||
}
|
||||
|
||||
// SerializeTo writes the serialized form of this layer into the
|
||||
// SerializationBuffer, implementing gopacket.SerializableLayer.
|
||||
// See the docs for gopacket.SerializableLayer for more info.
|
||||
func (i *ICMPv6Redirect) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
|
||||
if err := i.Options.SerializeTo(b, opts); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
buf, err := b.PrependBytes(36)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
copy(buf, lotsOfZeros[:4])
|
||||
copy(buf[4:], i.TargetAddress)
|
||||
copy(buf[20:], i.DestinationAddress)
|
||||
return nil
|
||||
}
|
||||
|
||||
// CanDecode returns the set of layer types that this DecodingLayer can decode.
|
||||
func (i *ICMPv6Redirect) CanDecode() gopacket.LayerClass {
|
||||
return LayerTypeICMPv6Redirect
|
||||
}
|
||||
|
||||
func (i ICMPv6Option) String() string {
|
||||
hd := hex.EncodeToString(i.Data)
|
||||
if len(hd) > 0 {
|
||||
hd = " 0x" + hd
|
||||
}
|
||||
|
||||
switch i.Type {
|
||||
case ICMPv6OptSourceAddress, ICMPv6OptTargetAddress:
|
||||
return fmt.Sprintf("ICMPv6Option(%s:%v)",
|
||||
i.Type,
|
||||
net.HardwareAddr(i.Data))
|
||||
case ICMPv6OptPrefixInfo:
|
||||
if len(i.Data) == 30 {
|
||||
prefixLen := uint8(i.Data[0])
|
||||
onLink := (i.Data[1]&0x80 != 0)
|
||||
autonomous := (i.Data[1]&0x40 != 0)
|
||||
validLifetime := time.Duration(binary.BigEndian.Uint32(i.Data[2:6])) * time.Second
|
||||
preferredLifetime := time.Duration(binary.BigEndian.Uint32(i.Data[6:10])) * time.Second
|
||||
|
||||
prefix := net.IP(i.Data[14:])
|
||||
|
||||
return fmt.Sprintf("ICMPv6Option(%s:%v/%v:%t:%t:%v:%v)",
|
||||
i.Type,
|
||||
prefix, prefixLen,
|
||||
onLink, autonomous,
|
||||
validLifetime, preferredLifetime)
|
||||
}
|
||||
case ICMPv6OptRedirectedHeader:
|
||||
// could invoke IP decoder on data... probably best not to
|
||||
break
|
||||
case ICMPv6OptMTU:
|
||||
if len(i.Data) == 6 {
|
||||
return fmt.Sprintf("ICMPv6Option(%s:%v)",
|
||||
i.Type,
|
||||
binary.BigEndian.Uint32(i.Data[2:]))
|
||||
}
|
||||
|
||||
}
|
||||
return fmt.Sprintf("ICMPv6Option(%s:%s)", i.Type, hd)
|
||||
}
|
||||
|
||||
// DecodeFromBytes decodes the given bytes into this layer.
|
||||
func (i *ICMPv6Options) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
|
||||
for len(data) > 0 {
|
||||
if len(data) < 2 {
|
||||
df.SetTruncated()
|
||||
return errors.New("ICMP layer less then 2 bytes for ICMPv6 message option")
|
||||
}
|
||||
|
||||
// unit is 8 octets, convert to bytes
|
||||
length := int(data[1]) * 8
|
||||
|
||||
if length == 0 {
|
||||
df.SetTruncated()
|
||||
return errors.New("ICMPv6 message option with length 0")
|
||||
}
|
||||
|
||||
if len(data) < length {
|
||||
df.SetTruncated()
|
||||
return fmt.Errorf("ICMP layer only %v bytes for ICMPv6 message option with length %v", len(data), length)
|
||||
}
|
||||
|
||||
o := ICMPv6Option{
|
||||
Type: ICMPv6Opt(data[0]),
|
||||
Data: data[2:length],
|
||||
}
|
||||
|
||||
// chop off option we just consumed
|
||||
data = data[length:]
|
||||
|
||||
*i = append(*i, o)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// SerializeTo writes the serialized form of this layer into the
|
||||
// SerializationBuffer, implementing gopacket.SerializableLayer.
|
||||
// See the docs for gopacket.SerializableLayer for more info.
|
||||
func (i *ICMPv6Options) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
|
||||
for _, opt := range []ICMPv6Option(*i) {
|
||||
length := len(opt.Data) + 2
|
||||
buf, err := b.PrependBytes(length)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
buf[0] = byte(opt.Type)
|
||||
buf[1] = byte(length / 8)
|
||||
copy(buf[2:], opt.Data)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func decodeICMPv6Echo(data []byte, p gopacket.PacketBuilder) error {
|
||||
i := &ICMPv6Echo{}
|
||||
return decodingLayerDecoder(i, data, p)
|
||||
}
|
||||
|
||||
func decodeICMPv6RouterSolicitation(data []byte, p gopacket.PacketBuilder) error {
|
||||
i := &ICMPv6RouterSolicitation{}
|
||||
return decodingLayerDecoder(i, data, p)
|
||||
}
|
||||
|
||||
func decodeICMPv6RouterAdvertisement(data []byte, p gopacket.PacketBuilder) error {
|
||||
i := &ICMPv6RouterAdvertisement{}
|
||||
return decodingLayerDecoder(i, data, p)
|
||||
}
|
||||
|
||||
func decodeICMPv6NeighborSolicitation(data []byte, p gopacket.PacketBuilder) error {
|
||||
i := &ICMPv6NeighborSolicitation{}
|
||||
return decodingLayerDecoder(i, data, p)
|
||||
}
|
||||
|
||||
func decodeICMPv6NeighborAdvertisement(data []byte, p gopacket.PacketBuilder) error {
|
||||
i := &ICMPv6NeighborAdvertisement{}
|
||||
return decodingLayerDecoder(i, data, p)
|
||||
}
|
||||
|
||||
func decodeICMPv6Redirect(data []byte, p gopacket.PacketBuilder) error {
|
||||
i := &ICMPv6Redirect{}
|
||||
return decodingLayerDecoder(i, data, p)
|
||||
}
|
||||
+355
@@ -0,0 +1,355 @@
|
||||
// Copyright 2012 Google, Inc. All rights reserved.
|
||||
// Copyright 2009-2011 Andreas Krennmair. All rights reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the LICENSE file in the root of the source
|
||||
// tree.
|
||||
|
||||
package layers
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/google/gopacket"
|
||||
)
|
||||
|
||||
type IGMPType uint8
|
||||
|
||||
const (
|
||||
IGMPMembershipQuery IGMPType = 0x11 // General or group specific query
|
||||
IGMPMembershipReportV1 IGMPType = 0x12 // Version 1 Membership Report
|
||||
IGMPMembershipReportV2 IGMPType = 0x16 // Version 2 Membership Report
|
||||
IGMPLeaveGroup IGMPType = 0x17 // Leave Group
|
||||
IGMPMembershipReportV3 IGMPType = 0x22 // Version 3 Membership Report
|
||||
)
|
||||
|
||||
// String conversions for IGMP message types
|
||||
func (i IGMPType) String() string {
|
||||
switch i {
|
||||
case IGMPMembershipQuery:
|
||||
return "IGMP Membership Query"
|
||||
case IGMPMembershipReportV1:
|
||||
return "IGMPv1 Membership Report"
|
||||
case IGMPMembershipReportV2:
|
||||
return "IGMPv2 Membership Report"
|
||||
case IGMPMembershipReportV3:
|
||||
return "IGMPv3 Membership Report"
|
||||
case IGMPLeaveGroup:
|
||||
return "Leave Group"
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
type IGMPv3GroupRecordType uint8
|
||||
|
||||
const (
|
||||
IGMPIsIn IGMPv3GroupRecordType = 0x01 // Type MODE_IS_INCLUDE, source addresses x
|
||||
IGMPIsEx IGMPv3GroupRecordType = 0x02 // Type MODE_IS_EXCLUDE, source addresses x
|
||||
IGMPToIn IGMPv3GroupRecordType = 0x03 // Type CHANGE_TO_INCLUDE_MODE, source addresses x
|
||||
IGMPToEx IGMPv3GroupRecordType = 0x04 // Type CHANGE_TO_EXCLUDE_MODE, source addresses x
|
||||
IGMPAllow IGMPv3GroupRecordType = 0x05 // Type ALLOW_NEW_SOURCES, source addresses x
|
||||
IGMPBlock IGMPv3GroupRecordType = 0x06 // Type BLOCK_OLD_SOURCES, source addresses x
|
||||
)
|
||||
|
||||
func (i IGMPv3GroupRecordType) String() string {
|
||||
switch i {
|
||||
case IGMPIsIn:
|
||||
return "MODE_IS_INCLUDE"
|
||||
case IGMPIsEx:
|
||||
return "MODE_IS_EXCLUDE"
|
||||
case IGMPToIn:
|
||||
return "CHANGE_TO_INCLUDE_MODE"
|
||||
case IGMPToEx:
|
||||
return "CHANGE_TO_EXCLUDE_MODE"
|
||||
case IGMPAllow:
|
||||
return "ALLOW_NEW_SOURCES"
|
||||
case IGMPBlock:
|
||||
return "BLOCK_OLD_SOURCES"
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
// IGMP represents an IGMPv3 message.
|
||||
type IGMP struct {
|
||||
BaseLayer
|
||||
Type IGMPType
|
||||
MaxResponseTime time.Duration
|
||||
Checksum uint16
|
||||
GroupAddress net.IP
|
||||
SupressRouterProcessing bool
|
||||
RobustnessValue uint8
|
||||
IntervalTime time.Duration
|
||||
SourceAddresses []net.IP
|
||||
NumberOfGroupRecords uint16
|
||||
NumberOfSources uint16
|
||||
GroupRecords []IGMPv3GroupRecord
|
||||
Version uint8 // IGMP protocol version
|
||||
}
|
||||
|
||||
// IGMPv1or2 stores header details for an IGMPv1 or IGMPv2 packet.
|
||||
//
|
||||
// 0 1 2 3
|
||||
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | Type | Max Resp Time | Checksum |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | Group Address |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
type IGMPv1or2 struct {
|
||||
BaseLayer
|
||||
Type IGMPType // IGMP message type
|
||||
MaxResponseTime time.Duration // meaningful only in Membership Query messages
|
||||
Checksum uint16 // 16-bit checksum of entire ip payload
|
||||
GroupAddress net.IP // either 0 or an IP multicast address
|
||||
Version uint8
|
||||
}
|
||||
|
||||
// decodeResponse dissects IGMPv1 or IGMPv2 packet.
|
||||
func (i *IGMPv1or2) decodeResponse(data []byte) error {
|
||||
if len(data) < 8 {
|
||||
return errors.New("IGMP packet too small")
|
||||
}
|
||||
|
||||
i.MaxResponseTime = igmpTimeDecode(data[1])
|
||||
i.Checksum = binary.BigEndian.Uint16(data[2:4])
|
||||
i.GroupAddress = net.IP(data[4:8])
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// 0 1 2 3
|
||||
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | Type = 0x22 | Reserved | Checksum |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | Reserved | Number of Group Records (M) |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | |
|
||||
// . Group Record [1] .
|
||||
// | |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | |
|
||||
// . Group Record [2] .
|
||||
// | |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | |
|
||||
// . Group Record [M] .
|
||||
// | |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | Record Type | Aux Data Len | Number of Sources (N) |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | Multicast Address |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | Source Address [1] |
|
||||
// +- -+
|
||||
// | Source Address [2] |
|
||||
// +- -+
|
||||
// | Source Address [N] |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | |
|
||||
// . Auxiliary Data .
|
||||
// | |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
|
||||
// IGMPv3GroupRecord stores individual group records for a V3 Membership Report message.
|
||||
type IGMPv3GroupRecord struct {
|
||||
Type IGMPv3GroupRecordType
|
||||
AuxDataLen uint8 // this should always be 0 as per IGMPv3 spec.
|
||||
NumberOfSources uint16
|
||||
MulticastAddress net.IP
|
||||
SourceAddresses []net.IP
|
||||
AuxData uint32 // NOT USED
|
||||
}
|
||||
|
||||
func (i *IGMP) decodeIGMPv3MembershipReport(data []byte) error {
|
||||
if len(data) < 8 {
|
||||
return errors.New("IGMPv3 Membership Report too small #1")
|
||||
}
|
||||
|
||||
i.Checksum = binary.BigEndian.Uint16(data[2:4])
|
||||
i.NumberOfGroupRecords = binary.BigEndian.Uint16(data[6:8])
|
||||
|
||||
recordOffset := 8
|
||||
for j := 0; j < int(i.NumberOfGroupRecords); j++ {
|
||||
if len(data) < recordOffset+8 {
|
||||
return errors.New("IGMPv3 Membership Report too small #2")
|
||||
}
|
||||
|
||||
var gr IGMPv3GroupRecord
|
||||
gr.Type = IGMPv3GroupRecordType(data[recordOffset])
|
||||
gr.AuxDataLen = data[recordOffset+1]
|
||||
gr.NumberOfSources = binary.BigEndian.Uint16(data[recordOffset+2 : recordOffset+4])
|
||||
gr.MulticastAddress = net.IP(data[recordOffset+4 : recordOffset+8])
|
||||
|
||||
if len(data) < recordOffset+8+int(gr.NumberOfSources)*4 {
|
||||
return errors.New("IGMPv3 Membership Report too small #3")
|
||||
}
|
||||
|
||||
// append source address records.
|
||||
for i := 0; i < int(gr.NumberOfSources); i++ {
|
||||
sourceAddr := net.IP(data[recordOffset+8+i*4 : recordOffset+12+i*4])
|
||||
gr.SourceAddresses = append(gr.SourceAddresses, sourceAddr)
|
||||
}
|
||||
|
||||
i.GroupRecords = append(i.GroupRecords, gr)
|
||||
recordOffset += 8 + 4*int(gr.NumberOfSources)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// 0 1 2 3
|
||||
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | Type = 0x11 | Max Resp Code | Checksum |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | Group Address |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | Resv |S| QRV | QQIC | Number of Sources (N) |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | Source Address [1] |
|
||||
// +- -+
|
||||
// | Source Address [2] |
|
||||
// +- . -+
|
||||
// | Source Address [N] |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
//
|
||||
// decodeIGMPv3MembershipQuery parses the IGMPv3 message of type 0x11
|
||||
func (i *IGMP) decodeIGMPv3MembershipQuery(data []byte) error {
|
||||
if len(data) < 12 {
|
||||
return errors.New("IGMPv3 Membership Query too small #1")
|
||||
}
|
||||
|
||||
i.MaxResponseTime = igmpTimeDecode(data[1])
|
||||
i.Checksum = binary.BigEndian.Uint16(data[2:4])
|
||||
i.SupressRouterProcessing = data[8]&0x8 != 0
|
||||
i.GroupAddress = net.IP(data[4:8])
|
||||
i.RobustnessValue = data[8] & 0x7
|
||||
i.IntervalTime = igmpTimeDecode(data[9])
|
||||
i.NumberOfSources = binary.BigEndian.Uint16(data[10:12])
|
||||
|
||||
if len(data) < 12+int(i.NumberOfSources)*4 {
|
||||
return errors.New("IGMPv3 Membership Query too small #2")
|
||||
}
|
||||
|
||||
for j := 0; j < int(i.NumberOfSources); j++ {
|
||||
i.SourceAddresses = append(i.SourceAddresses, net.IP(data[12+j*4:16+j*4]))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// igmpTimeDecode decodes the duration created by the given byte, using the
|
||||
// algorithm in http://www.rfc-base.org/txt/rfc-3376.txt section 4.1.1.
|
||||
func igmpTimeDecode(t uint8) time.Duration {
|
||||
if t&0x80 == 0 {
|
||||
return time.Millisecond * 100 * time.Duration(t)
|
||||
}
|
||||
mant := (t & 0x70) >> 4
|
||||
exp := t & 0x0F
|
||||
return time.Millisecond * 100 * time.Duration((mant|0x10)<<(exp+3))
|
||||
}
|
||||
|
||||
// LayerType returns LayerTypeIGMP for the V1,2,3 message protocol formats.
|
||||
func (i *IGMP) LayerType() gopacket.LayerType { return LayerTypeIGMP }
|
||||
func (i *IGMPv1or2) LayerType() gopacket.LayerType { return LayerTypeIGMP }
|
||||
|
||||
func (i *IGMPv1or2) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
|
||||
if len(data) < 8 {
|
||||
return errors.New("IGMP Packet too small")
|
||||
}
|
||||
|
||||
i.Type = IGMPType(data[0])
|
||||
i.MaxResponseTime = igmpTimeDecode(data[1])
|
||||
i.Checksum = binary.BigEndian.Uint16(data[2:4])
|
||||
i.GroupAddress = net.IP(data[4:8])
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (i *IGMPv1or2) NextLayerType() gopacket.LayerType {
|
||||
return gopacket.LayerTypeZero
|
||||
}
|
||||
|
||||
func (i *IGMPv1or2) CanDecode() gopacket.LayerClass {
|
||||
return LayerTypeIGMP
|
||||
}
|
||||
|
||||
// DecodeFromBytes decodes the given bytes into this layer.
|
||||
func (i *IGMP) DecodeFromBytes(data []byte, df gopacket.DecodeFeedback) error {
|
||||
if len(data) < 1 {
|
||||
return errors.New("IGMP packet is too small")
|
||||
}
|
||||
|
||||
// common IGMP header values between versions 1..3 of IGMP specification..
|
||||
i.Type = IGMPType(data[0])
|
||||
|
||||
switch i.Type {
|
||||
case IGMPMembershipQuery:
|
||||
i.decodeIGMPv3MembershipQuery(data)
|
||||
case IGMPMembershipReportV3:
|
||||
i.decodeIGMPv3MembershipReport(data)
|
||||
default:
|
||||
return errors.New("unsupported IGMP type")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// CanDecode returns the set of layer types that this DecodingLayer can decode.
|
||||
func (i *IGMP) CanDecode() gopacket.LayerClass {
|
||||
return LayerTypeIGMP
|
||||
}
|
||||
|
||||
// NextLayerType returns the layer type contained by this DecodingLayer.
|
||||
func (i *IGMP) NextLayerType() gopacket.LayerType {
|
||||
return gopacket.LayerTypeZero
|
||||
}
|
||||
|
||||
// decodeIGMP will parse IGMP v1,2 or 3 protocols. Checks against the
|
||||
// IGMP type are performed against byte[0], logic then iniitalizes and
|
||||
// passes the appropriate struct (IGMP or IGMPv1or2) to
|
||||
// decodingLayerDecoder.
|
||||
func decodeIGMP(data []byte, p gopacket.PacketBuilder) error {
|
||||
if len(data) < 1 {
|
||||
return errors.New("IGMP packet is too small")
|
||||
}
|
||||
|
||||
// byte 0 contains IGMP message type.
|
||||
switch IGMPType(data[0]) {
|
||||
case IGMPMembershipQuery:
|
||||
// IGMPv3 Membership Query payload is >= 12
|
||||
if len(data) >= 12 {
|
||||
i := &IGMP{Version: 3}
|
||||
return decodingLayerDecoder(i, data, p)
|
||||
} else if len(data) == 8 {
|
||||
i := &IGMPv1or2{}
|
||||
if data[1] == 0x00 {
|
||||
i.Version = 1 // IGMPv1 has a query length of 8 and MaxResp = 0
|
||||
} else {
|
||||
i.Version = 2 // IGMPv2 has a query length of 8 and MaxResp != 0
|
||||
}
|
||||
|
||||
return decodingLayerDecoder(i, data, p)
|
||||
}
|
||||
case IGMPMembershipReportV3:
|
||||
i := &IGMP{Version: 3}
|
||||
return decodingLayerDecoder(i, data, p)
|
||||
case IGMPMembershipReportV1:
|
||||
i := &IGMPv1or2{Version: 1}
|
||||
return decodingLayerDecoder(i, data, p)
|
||||
case IGMPLeaveGroup, IGMPMembershipReportV2:
|
||||
// leave group and Query Report v2 used in IGMPv2 only.
|
||||
i := &IGMPv1or2{Version: 2}
|
||||
return decodingLayerDecoder(i, data, p)
|
||||
default:
|
||||
}
|
||||
|
||||
return errors.New("Unable to determine IGMP type.")
|
||||
}
|
||||
+325
@@ -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
|
||||
}
|
||||
+707
@@ -0,0 +1,707 @@
|
||||
// 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) (h *ipv6HeaderTLVOption) {
|
||||
h = &ipv6HeaderTLVOption{}
|
||||
if data[0] == 0 {
|
||||
h.ActualLength = 1
|
||||
return
|
||||
}
|
||||
h.OptionType = data[0]
|
||||
h.OptionLength = data[1]
|
||||
h.ActualLength = int(h.OptionLength) + 2
|
||||
h.OptionData = data[2:h.ActualLength]
|
||||
return
|
||||
}
|
||||
|
||||
func serializeTLVOptionPadding(data []byte, padLength int) {
|
||||
if padLength <= 0 {
|
||||
return
|
||||
}
|
||||
if padLength == 1 {
|
||||
data[0] = 0x0
|
||||
return
|
||||
}
|
||||
tlvLength := uint8(padLength) - 2
|
||||
data[0] = 0x1
|
||||
data[1] = tlvLength
|
||||
if tlvLength != 0 {
|
||||
for k := range data[2:] {
|
||||
data[k+2] = 0x0
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// If buf is 'nil' do a serialize dry run
|
||||
func serializeIPv6HeaderTLVOptions(buf []byte, options []*ipv6HeaderTLVOption, fixLengths bool) int {
|
||||
var l int
|
||||
|
||||
dryrun := buf == nil
|
||||
length := 2
|
||||
for _, opt := range options {
|
||||
if fixLengths {
|
||||
x := int(opt.OptionAlignment[0])
|
||||
y := int(opt.OptionAlignment[1])
|
||||
if x != 0 {
|
||||
n := length / x
|
||||
offset := x*n + y
|
||||
if offset < length {
|
||||
offset += x
|
||||
}
|
||||
if length != offset {
|
||||
pad := offset - length
|
||||
if !dryrun {
|
||||
serializeTLVOptionPadding(buf[length-2:], pad)
|
||||
}
|
||||
length += pad
|
||||
}
|
||||
}
|
||||
}
|
||||
if dryrun {
|
||||
l = opt.serializeTo(nil, fixLengths, true)
|
||||
} else {
|
||||
l = opt.serializeTo(buf[length-2:], fixLengths, false)
|
||||
}
|
||||
length += l
|
||||
}
|
||||
if fixLengths {
|
||||
pad := length % 8
|
||||
if pad != 0 {
|
||||
if !dryrun {
|
||||
serializeTLVOptionPadding(buf[length-2:], pad)
|
||||
}
|
||||
length += pad
|
||||
}
|
||||
}
|
||||
return length - 2
|
||||
}
|
||||
|
||||
type ipv6ExtensionBase struct {
|
||||
BaseLayer
|
||||
NextHeader IPProtocol
|
||||
HeaderLength uint8
|
||||
ActualLength int
|
||||
}
|
||||
|
||||
func decodeIPv6ExtensionBase(data []byte, 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
|
||||
}
|
||||
offset := 2
|
||||
for offset < i.ActualLength {
|
||||
opt := decodeIPv6HeaderTLVOption(data[offset:])
|
||||
i.Options = append(i.Options, (*IPv6HopByHopOption)(opt))
|
||||
offset += opt.ActualLength
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func decodeIPv6HopByHop(data []byte, p gopacket.PacketBuilder) error {
|
||||
i := &IPv6HopByHop{}
|
||||
err := i.DecodeFromBytes(data, p)
|
||||
p.AddLayer(i)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return p.NextDecoder(i.NextHeader)
|
||||
}
|
||||
|
||||
// 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 := decodeIPv6HeaderTLVOption(data[offset:])
|
||||
i.Options = append(i.Options, (*IPv6DestinationOption)(opt))
|
||||
offset += opt.ActualLength
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func decodeIPv6Destination(data []byte, p gopacket.PacketBuilder) error {
|
||||
i := &IPv6Destination{}
|
||||
err := i.DecodeFromBytes(data, p)
|
||||
p.AddLayer(i)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return p.NextDecoder(i.NextHeader)
|
||||
}
|
||||
|
||||
// SerializeTo writes the serialized form of this layer into the
|
||||
// SerializationBuffer, implementing gopacket.SerializableLayer.
|
||||
// See the docs for gopacket.SerializableLayer for more info.
|
||||
func (i *IPv6Destination) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
|
||||
var bytes []byte
|
||||
var err error
|
||||
|
||||
o := make([]*ipv6HeaderTLVOption, 0, len(i.Options))
|
||||
for _, v := range i.Options {
|
||||
o = append(o, (*ipv6HeaderTLVOption)(v))
|
||||
}
|
||||
|
||||
l := serializeIPv6HeaderTLVOptions(nil, o, opts.FixLengths)
|
||||
bytes, err = b.PrependBytes(l)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
serializeIPv6HeaderTLVOptions(bytes, o, opts.FixLengths)
|
||||
|
||||
length := len(bytes) + 2
|
||||
if length%8 != 0 {
|
||||
return errors.New("IPv6Destination actual length must be multiple of 8")
|
||||
}
|
||||
bytes, err = b.PrependBytes(2)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
bytes[0] = uint8(i.NextHeader)
|
||||
if opts.FixLengths {
|
||||
i.HeaderLength = uint8((length / 8) - 1)
|
||||
}
|
||||
bytes[1] = uint8(i.HeaderLength)
|
||||
return nil
|
||||
}
|
||||
|
||||
func checkIPv6Address(addr net.IP) error {
|
||||
if len(addr) == net.IPv6len {
|
||||
return nil
|
||||
}
|
||||
if len(addr) == net.IPv4len {
|
||||
return errors.New("address is IPv4")
|
||||
}
|
||||
return fmt.Errorf("wrong length of %d bytes instead of %d", len(addr), net.IPv6len)
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
+68
@@ -0,0 +1,68 @@
|
||||
// Copyright 2012 Google, Inc. All rights reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the LICENSE file in the root of the source
|
||||
// tree.
|
||||
|
||||
package layers
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"github.com/google/gopacket"
|
||||
)
|
||||
|
||||
// IPSecAH is the authentication header for IPv4/6 defined in
|
||||
// http://tools.ietf.org/html/rfc2402
|
||||
type IPSecAH struct {
|
||||
// While the auth header can be used for both IPv4 and v6, its format is that of
|
||||
// an IPv6 extension (NextHeader, PayloadLength, etc...), so we use ipv6ExtensionBase
|
||||
// to build it.
|
||||
ipv6ExtensionBase
|
||||
Reserved uint16
|
||||
SPI, Seq uint32
|
||||
AuthenticationData []byte
|
||||
}
|
||||
|
||||
// LayerType returns LayerTypeIPSecAH.
|
||||
func (i *IPSecAH) LayerType() gopacket.LayerType { return LayerTypeIPSecAH }
|
||||
|
||||
func decodeIPSecAH(data []byte, p gopacket.PacketBuilder) error {
|
||||
i := &IPSecAH{
|
||||
ipv6ExtensionBase: ipv6ExtensionBase{
|
||||
NextHeader: IPProtocol(data[0]),
|
||||
HeaderLength: data[1],
|
||||
},
|
||||
Reserved: binary.BigEndian.Uint16(data[2:4]),
|
||||
SPI: binary.BigEndian.Uint32(data[4:8]),
|
||||
Seq: binary.BigEndian.Uint32(data[8:12]),
|
||||
}
|
||||
i.ActualLength = (int(i.HeaderLength) + 2) * 4
|
||||
i.AuthenticationData = data[12:i.ActualLength]
|
||||
i.Contents = data[:i.ActualLength]
|
||||
i.Payload = data[i.ActualLength:]
|
||||
p.AddLayer(i)
|
||||
return p.NextDecoder(i.NextHeader)
|
||||
}
|
||||
|
||||
// IPSecESP is the encapsulating security payload defined in
|
||||
// http://tools.ietf.org/html/rfc2406
|
||||
type IPSecESP struct {
|
||||
BaseLayer
|
||||
SPI, Seq uint32
|
||||
// Encrypted contains the encrypted set of bytes sent in an ESP
|
||||
Encrypted []byte
|
||||
}
|
||||
|
||||
// LayerType returns LayerTypeIPSecESP.
|
||||
func (i *IPSecESP) LayerType() gopacket.LayerType { return LayerTypeIPSecESP }
|
||||
|
||||
func decodeIPSecESP(data []byte, p gopacket.PacketBuilder) error {
|
||||
i := &IPSecESP{
|
||||
BaseLayer: BaseLayer{data, nil},
|
||||
SPI: binary.BigEndian.Uint32(data[:4]),
|
||||
Seq: binary.BigEndian.Uint32(data[4:8]),
|
||||
Encrypted: data[8:],
|
||||
}
|
||||
p.AddLayer(i)
|
||||
return nil
|
||||
}
|
||||
+218
@@ -0,0 +1,218 @@
|
||||
// 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)})
|
||||
)
|
||||
|
||||
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,
|
||||
})
|
||||
)
|
||||
+213
@@ -0,0 +1,213 @@
|
||||
// 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"
|
||||
|
||||
"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 {
|
||||
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
|
||||
}
|
||||
+98
@@ -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)
|
||||
}
|
||||
+193
@@ -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
|
||||
}
|
||||
+1585
File diff suppressed because it is too large
Load Diff
+80
@@ -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)
|
||||
}
|
||||
+182
@@ -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)
|
||||
}
|
||||
+619
@@ -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) < 4 {
|
||||
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)
|
||||
}
|
||||
+150
@@ -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
|
||||
}
|
||||
+87
@@ -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
|
||||
}
|
||||
+611
@@ -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
|
||||
}
|
||||
+416
@@ -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 *
|
||||
//******************************************************************************
|
||||
+680
@@ -0,0 +1,680 @@
|
||||
// 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"
|
||||
)
|
||||
|
||||
// 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
|
||||
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
|
||||
}
|
||||
|
||||
// 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
|
||||
links := binary.BigEndian.Uint16(data[22:24])
|
||||
content = RouterLSAV2{
|
||||
Flags: data[20],
|
||||
Links: links,
|
||||
Routers: routers,
|
||||
}
|
||||
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 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.")
|
||||
}
|
||||
+76
@@ -0,0 +1,76 @@
|
||||
// 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"
|
||||
)
|
||||
|
||||
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 {
|
||||
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
|
||||
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)
|
||||
}
|
||||
+154
@@ -0,0 +1,154 @@
|
||||
// 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,
|
||||
}
|
||||
|
||||
// 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))
|
||||
}
|
||||
+88
@@ -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
|
||||
}
|
||||
+60
@@ -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
|
||||
}
|
||||
+146
@@ -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 }
|
||||
+1069
File diff suppressed because it is too large
Load Diff
+93
@@ -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)})
|
||||
}
|
||||
+746
@@ -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
|
||||
}
|
||||
+2480
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user